평소 궁금했지만, 자세히 찾아보지는 않았던 JVM
. 이참에 확실히 이해를 한번 해 보자는 마음가짐으로 깊게 파고들어보자.
썸네일이 전체 JVM의 구조이다. 잘 이해가 안될테니 차근차근 보자
JVM(Java Virtual Machine)으로, 자바를 실행하기 위한 가상 컴퓨터이다.
일반 어플리케이션과 다르게, 자바 어플리케이션이 가지는 가장 큰 특징은 OS에 종속적이지 않다는 것이고, 이것은 JVM덕분에 존재하는 특징이다.
위의 사진처럼, 자바 프로그램은 JVM
을 거쳐서 동작한다. JVM
이 자바 프로그램을 OS에 맞는 기계어로 변환을 해 준다. 다만 JVM
은 OS에 종속적이므로, 운영체제에 맞는 JVM이 필요한 것은 당연하다.
JVM
도 유저메모리에 존재하는, 하나의 프로세스이다.
우리가 작성한 자바 코드(*.java)는 JVM
이 바로 이해하지 못한다. 따라서 Java Compiler를 거쳐 ㅈJava Bytecode인 (*.class)로 변환되어야 한다. 그래야 JVM이 Java Bytecode를 이해하고, OS가 이해할 수 있는 기계어로 변환이 된다.
Java Compiler
여기서 말한 자바 컴파일러는 JDK의bin
에 존재하는javac.exe
이다.
한번쯤 javac myProgram.java
커맨드를 통해 자바파일을 클래스파일로 변환해 본 적이 있을 것이다. 커맨드를 통해 실행하든, 컴파일러를 통하든 .class
로 된 Java Bytecode만 존재한다면 jvm
위에서 자바 프로그램을 실행할 수 있다.
바이트코드는, Execution Engine
내부에 존재하는 Jit Compiler
또는 인터프리터
에 의해 기계어(=바이너리 코드, 네이티브코드)로 변환되어 CPU가 이해할 수 있게 된다.
인터프리터와 JIT Compiler는 둘 다 바이트코드를 기계어로 변환하는 역할을 수행한다.
인터프리터는 자바 바이트코드를 명령어 단위로 한 줄씩 읽어서 수행하고, 속도가 빠르다.
컴파일러는 전체 코드를 한번에 기계어로 변환하여 캐시에 보관하기 때문에, 컴파일 과정이 오래 걸리지만 컴파일이 되면 인터프리터보다 빠르다.
따라서 JVM은 기본적으로 인터프리터를 사용하고, 자주 사용되는 코드를 JIT Complier가 체크하였다가 컴파일하여 사용한다.
결론적으로 인터프리터의 단점을 보완하면서, 속도 성능을 개선하기 위해 인터프리팅 방식과 컴파일 방식을 둘 다 사용한다고 한다.
자세한 것은 이 블로그를 참고하자.
JVM
외부에서 내부로 클래스 파일(*.class)을 로드하는 역할이다.
자바 프로그램을 처음 실행할 때, 클래스 파일(Java Bytecode!!)을 전부 읽어오기 때문에 해당 파일을 전부 가져다 주는 역할이다.
더이상 사용되지 않는 인스턴스를 찾아 메모리에서 삭제한다.
가비지 콜렉터는 자바에서 엄청 중요한 영역이므로, 따로 찾아보길 권한다.(나 포함)
위의 두개의사진을 같이 보는것을 추천한다. Method 영역
과 heap 영역
은 jvm프로세스에 존재하는 공유자원이며, 각 스레드는 독립적으로 PC
, JVM Stack
, Native Stack
을 가진다.
Runtime Data Area는 프로그램을 수행하기 위해, OS에서 할당받은 메모리 공간이다.
클래스로더에 의해 내가 작성한 클래스가 로드되는 공간이다.
또는 static
으로 선언한 변수나 상수 처럼, 클래스가 로드될 때 한번만 생성되는 부분이 이 부분이다.
Method 영역
에 존재하는 클래스를 생성할 때는 new
명령어를 통해 인스턴스를 생성하게 된다.
즉 객체(인스턴스)가 저장되는 공간이다.
인스턴스가 많아지면, 힙 영역의 크기가 유동적으로 늘어나거나 줄어들 수 있다. 이것을 관리하는 주체는 가비지 컬렉터이다.
즉, 가비지 컬렉터에 타겟이 되는 부분은 Heap Area라는 것이다.
→ 실행되면서, 인스턴스가 많아지면 유동적으로 크기가 증가함. **가비지컬렉터에 의해서 늘리고 줄이고 관리된다.
lazyHolder
와 같은 싱글톤 전략을 사용하더라도, 싱글톤 인스턴스가 Heap Area에 생성되므로 동시성 문제를 잘 제어해야 하는 이유이다.이
Heap 영역이 어떻게 생겼는지 조금 더 알아보자.
Heap 영역의 구조
1. Permanent Generation
클래스와 객체, 메서드 등에 대한 메타데이터가 저장되는 영역이다.
2. New/Young Generation
생성된 지 얼마 되지 않은 젊은 인스턴스가 위치하는 곳이다.
따라서 생명 주기가 짧기 때문에, 주로 GC의 대상이 된다.
- Eden : 객체들이 최초로 생성되는 공간
- Survivor : Eden에서 참조되는 객체들이 저장되는 공간
3. Old Generaion
생명 주기가 긴 인스턴스이다.Young generation
에서 오랫동안 GC의 대상이 되지 않아 살아남은 인스턴스이다.
아래 세 개의 영역은, 스레드가 생성될 때마다 생성되는 스레드-독립적 영역이다.
프로세스 이미지의 구성 요소인(code, data, stack, heap, pcb)에서 stack
과 동일한 역할을 한다.
지역변수, 매개변수 등 임시로 저장되는 값을 저장하는 공간이다.
현재 스레드가 실행하고있는 코드의 주소 정보를 가지는 공간이다.
자바가 아닌 언어로 작성되었지만 우리가 사용하는 부분에서 필요한 영역이다.
네이티브 코드(=기계어, 바이너리 코드)에서 사용하는 스택이다.
추후 JNI
와 연관이 있다는데, 추후에 알아보도록 하자..
개멋져