Java 가상머신

Virtual Machine, Byte Code, Stack, Heap, Garbage Collector

 
 

Byte Code (바이트 코드)

C 언어 vs 기계어 코드

기계어는 "0101110101010.." 형태의 2진코드로 이루어져 있는 언어로, 기계가 읽는 언어를 말한다. 사람이 2진수로 직접 코딩하는 것이 어렵기 때문에, 사람이 알아볼 수 있는 C언어로 만든 후, 컴파일을 해서 기계어로 변경하는 것이다.

  1. C 언어 코딩 (ex, #include<stdio.h> ... ) / .c 파일
  2. 컴파일 (ex, gcc)
  3. 기계어 프로그램 (ex, 010111000100 ...) / .o 파일
  4. 기계어 프로그램을 OS에서 실행 (ex, Windows에서 실행)

 

Java 언어 vs 바이트 코드

Java 언어의 경우 C 언어와는 조금 다르다. 사람이 알아볼 수 있는 Java언어로 만든 후, 컴파일을 해서 바이트코드로 변경하는 것이다.

  1. Java 언어 코딩 (ex, public class Joker { ... ) / .java 파일
  2. 컴파일 (ex, javac)
  3. 바이트코드 프로그램 (ex, aload_0 ...) / .class 파일
  4. 바이트코드 프로그램을 JVM에서 실행 (ex, JVM에서 실행)

 

C언어의 기계와 코드와 Java 언어의 바이트 코드는 둘다 컴파일을 통해서 만들어진 것이지만, 둘의 형태가 다르고 실행하는 주체가 다르기 때문에 둘을 다르게 부르는 것이다. 컴파일된 C 언어의 기계어의 경우 Machine(컴퓨터)에서 바로 실행 가능한 형태이지만, 컴파일된 Java의 바이트코드 경우에는 Machine(컴퓨터)위의 Java Virtual Machine(JVM, 자바가상머신)이라는 프로그램에서 바로 실행가능한 형태라는 것이다. 따라서 컴파일된 Java의 바이트코드는 중간언어라고도 말하기도 말한다.

 

※ 참고, 컴파일과 링킹

컴파일(Compile)은 만든 프로그램을 기계어로 바꾸는 과정을 말한다.
링킹(Linking)은 프로그램을 만들때, 다른 라이브러리를 사용한다면, 기계어로 된 그 라이브러리를 추가하는 과정을 말한다.

 

 

Java Virtual Machine (자바 가상 머신)

Java가 등장하기 전

Java가 C언어와 가장 큰 차이 중 하나는 자바가상머신이다. 다른 언어로 만든 프로그램은 Window, Linux, Unix 중 돌아가는 운영환경에 맞게 각각 컴파일을 해주어야 한다. 다시 말해서, 하나의 프로그램을 만들 때, Window용으로 만들던가 Unix용으로 만들던가 해야된다는 이야기이다.

 

예를들어, C언어로 카카오톡(KakaoTalk) 프로그램을 만든다면, Windows용, Linux용, Android용으로 따로 만들어야 한다는 것이다. 그런데, Java는 카카오톡 프로그램 하나만 만들어서 Windows, Linux, Android 모두 실행 가능하도록 해준다. 따라서 C언어를 사용할 때의 불편한 점을 해결했다.

 

Java가 등장한 후

Java는 위의 불편한 점을 해결하기 위해 OS위에서 돌아가는 완전한 기계어로 컴파일하지 않고, 중간까지만 컴파일을 하는것이다. 나머지는 자바가상머신(JVM)에게 맡기는 것이다. 이 이유 때문에 Java를 컴파일 언어, 인터프리터 언어라고도 하는 것이다. 그 중간까지 컴파일 한 상태의 파일을 자바에서 확장자는 .class로 만들어지는데, 이를 바이트 코드라고한다. 하나의 Java 프로그램을 만들고 PC에 Window, Linux, Unix, Android에서 실행이 가능한 JVM이 설치되어 있다면, Java 프로그램은 해당 JVM 위에서 실행이 되는 것이다.

 

그로 인해, C언어가 아닌 Java로 카카오톡(KakaoTalk) 프로그램을 만들어서 JVM위에서 실행가능하도록 만든다면, Windows용, Linux용, Android용 JVM이 PC에서 동작하도록 알아서 실행시켜주는 것이다.

 

 

바로 JVM이 있기 때문에 지금 Java를 많이 사용하는 것이다. 처음 java가 나왔을 떄는 PC에서 바로 실행되는 것이 아닌 그 위의 프로그램인 JVM을 한번더 거쳐야 하기 때문에, 느리다는 말들이 있었지만, 지금은 사람이 체감할 정도로 느리지는 않는다. CPU나 메모리의 기술이 높아졌기 때문이다.

 

 

 

Stack Memory, Heap Memory

Stack과 Heap의 차이

Stack(스택)과 Heap(힙) 메모리 구조는 컴퓨터 과목에서 많이 등장하는 내용이다. 스택 메모리는 컴파일 할때 미리 계산이 되는 반면, 힙 메모리는 컴파일 할때가 아닌, 프로그램을 실행할 때 메모리를 빌려서 동적으로 할당된다. (예: new)
java에서는 대부분을 new를 사용하기 때문에 힙(heap) 메모리를 쓰는거 같지만, 스택(stack) 메모리를 사용하지 않고는 프로그램이 돌아갈수 없다. 이 말이 어떤 의미인지 아래 샘플 코드를 보고 이해해보자.

 

public class TopMemory {
	private int sum; 
	private int minus; 

	public int calcData(int a, int b){
		int c = a + b; 
		int d = a - b; 
		sum = c; 
		minus = d; 
		return sum;
	} 

	public static void main(String[] args){
		TopMemory t = new TopMemory();
		int s = t.calcData(100, 200); 
		System.out.println("결과는 :" + s);
	}
}


든 함수(=Method)를 호출하기 전에는 반드시 사용할 메모리를 할당받고나서 시작한다.

  1. 메소드 호출
  2. 사용할 메모리 확보
  3. 확보한 메모리안에서 모든 작업해결

 

main 함수를 호출하는데 확보해야 하는 메모리

  정적메모리(8바이트) 필요 - 참조변수 t, 변수 s
  동적메모리(8바이트) 필요 - new TopMemory()

 

calcData 함수를 호출하는데 확보해야 하는 메모리

  정적메모리(16바이트) 필요 - a, b, c, d
  동적메모리(8바이트)필요 - sum, minus

 

개발을 할 때, 어떤 경우에 정적(stack), 동적(heap)메모리가 할당되는지 아는 것은 중요하다. Java에서는 정적, 동적인 메모리를 구분하면서 사용할 필요가 사실은 거의 없다. C언어에서는 정확히 알고 코딩을 했었는데, 이 부분에 대해서는 개발자에게 정말 편한 이점인 것 같다. 메모리 공부를 따로 하지 않아도 프로그램을 만드는데 별지장 없지만, 기본적인 스택과 힙 메모리가 어떻게 관리되는지를 아는 것과 모르는 것은 분명 차이가 있을 것이라고 생각한다.

 

 

Garbage Collector (가비지 콜렉터)

생성(new)을 하는데 (delete)를 사용하지 않는 이유

Java에서는 객체에 메모리를 할당할 때 new를 사용한다. 하지만 C 언어처럼 delete를 사용해서 해제하지 않는다. 이것이 가능한 이유는 Java 프로그램을 동작시키는 JVM이 Garbage Collector라는 스레드(thread)를 지원해주기 때문이다. Garbage Collector는 new로 생성한 메모리에 대해 알아서 판단해서 필요없을 때 해제시킨다.