본문 바로가기

JAVA

JVM의 구조와 자바의 메모리 구조

자바 애플리케이션이 실행될 때 JVM에서 일어나는 일, 과정은 어떻게 될까?


우리가 자바 애플리케이션을 실행할 때 JAVA와 OS 사이에서 동작하는 JVM(Java Virtual Machine)이라는 소프트웨어로 구현한 머신이 있다.

1. JVM(Java Virtual Machine) 이란?


Java Virtual Machine 은 자바 애플리케이션을 class loader 를 통해 읽어 들여 자바 API와 함께 실행하는 역할을 하며, Stack 기반으로 동작한다.

우리는 한정된 메모리를 사용해서 최고의 성능을 내야하기 때문에 JVM의 이해는 필수적이라고 생각한다.

자바는 컴파일러를 통해 .class 파일로 JVM을 위한 기계어로 변환을 하여 Interpreter에서 특정 기계에서 작동할수 있게 번역해주는 과정을 거친다.

이는 플랫폼에 종속되지 않으며, 컴퓨터와 프로그램사이의 일종의 버퍼 역할을 하여 보안상에 이점이 있다.

그림을 통하여 자세하게 살펴보자

자바 프로그램의 실행 과정
자바 프로그램의 실행과정

1. 자바 프로그램이 시작되면 JVM은 OS로부터 이 프로그램이 필요로하는 메모리를 할당받는다.
JVM은 이 메모리를 용도에 따라 영역을 나눠 관리한다.

2. Java Compiler 가 자바 소스코드(*.java)를 읽어들여 자바 바이트 코드(*.class)로 변환시킨다.

3. Class Loader 를 통해 class 파일들을 JVM으로 로딩한다.

4. 로딩된 class 파일들은 Execution engine 을 통해 해석된다.

5. 해석된 바이트 코드는 Runtime Data Area 에 배치되어 실질적인 수행이 이뤄지게 된다.

JVM의 구성

  • Class Loader (클래스 로더) :

    JVM 내로 *.class 파일을 로드하고, 링크를 통해서 배치하는 작업을 수행하는 모듈이다. Runtime 시에 동적으로 클래스를 로드한다. jar 파일내에 저장된 클래스들을 JVM위에 탑재하고 사용하지 않는 클래스들은 메모리에서 삭제한다. 자바는 런타임에 참조한다. 즉, 클래스를 처음 참조할 때, 해당 클래스를 로드하고 링크한다는 것이다.

  • Execution Engine (실행 엔진) :

    클래스를 실행하는 역할으로, 클래스 로더가 JVM내의 런타임 데이터 영역에 바이트 코드를 배치시키고, 실행 엔진이 이를 실행한다. 자바 바이트 코드는 기계가 쉽게 알아들을 수 있는 언어라기 보다 인간이 보기 편한 형태로 기술된 것이다. 실행 엔진은 바이트코드를 JVM 내부에서 기계가 실행할 수 있는 언어로 변경하는데 2가지 방식을 사용한다.

    1. Interpreter (인터프리터) :
    실행엔진은 자바 바이트 코드를 명령어 단위로 읽어서 실행하게 되는데. 한 줄씩 수행하기 때문에 느리다는 단점이 있다.

    2. JIT (Just - In - Time) :
    인터프리터의 단점을 보완하기 위해 도입된 JIT 컴파일러는 인터프리터 방식으로 실행하다가 내부적으로 해당 메서드가 얼마나 자주 수행되는지 체크하고, 일정 정도를 넘을 때에만 컴파일을 수행한다.

  • Garbage collector (가비지 컬렉터) :

    C/C++ 과 다르게 JAVA 는 개발자가 직접메모리를 관리하는 작업을 수행하지 않는다. 가비지 컬렉터가 이를 수행 하는데 이는 추후 포스팅을 통해 다루겠다.

2. Runtime Data Area


Runtime Data Area 는 자바 프로그램을 수행하기 위해서 OS에서 할당받은 메모리 공간이다.

  • PC Register :
    Thread 가 시작될때 생성될 때마다 생성되는 공간으로 쓰레드마다 하나씩 존재한다. 현재 수행중인 JVM 명령의 주소를 가지며 어떤 부분을 어떤 명령으로 실행할지에대한 기록을 하는부분

  • JVM Stack : 
    프로그램의 실행과정에서 임시로 할당되었다가 메소드를 빠져나가면 바로 소멸되는 특성의 데이터를 저장하기 위한 영역이다. 메소드 호출시 마다 각각의 Stack 프레임이 생성되며 메소드 수행이 끝나면 프레임 별로 삭제를 한다. 또한 호출된 메소드의 매개변수, 지역변수, 리턴 값 등 연산시 수행되는 값들을 임시로 저장한다.

  • Native Method Stack : 
    자바 프로그램이 컴파일되어 생성되는 바이트코드가 아닌 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행 시키는 영역. 즉 JAVA 언어가 아닌 다른 언어로 작성된 코드를 위한 공간이다. 일반 프로그램 처럼 커널이 스택을 잡아서 독자적으로 실행시키는 영역으로 이부분을 통해 C code를 실행시켜서 커널에 접근이 가능하다.

  • Method Area (Class Area, Static Area) : 
    클래스 정보를 처음 메모리 공간에 올릴 때 초기화되는 대상을 저장하기위한 메모리 공간이다. 자바 프로그램은 main 메소드의 호출을 시작으로 계속된 메소드의 호출로 흐름을 이어간다. 따라서 사실상 컴파일된 바이트코드의 대부분이 메소드 바이트코드이기 때문에 거의 모든 바이트 코드가 올라간다고 봐도 무방하다.

  • Heap : 
    클래스 데이터를 위한 공간으로 Method Area가 있다면, Heap 은 객체(인스턴스)를 위해 있는 공간이다. new 연산자로 생성된 객체와 배열을 저장한다. 힙은 세부분으로 나눌 수 있다. 


    1. New/Young 영역 :
    - Eden : 객체들이 최초로 생성되는 공간
    - Survivor : Eden에서 참조되는 객체들이 저장되는 공간

    2. Old 영역 : 
    New 영역에서 일정시간 참조되고있는, 살아남은 객체들이 저장되는 Eden 역역에 객체가 가득차면서 첫번째 GC(minor GC)가 발생한다. Eden 영역에 있는 값들을 Survivor 영역중 하나에 복사하고 이 영역을 제외한 나머지 영역의 객체를 삭제한다.

    3.  Permananent Generation : 
    생성된 객체들 정보의 주소값이 저장된 공간이다. 클래스로더에 의해 로드되는 Class, Method 등에 대한 Meta 정보가 저장되는 영역

    Minor GC :
    새로 생성된 대부분의 객체(인스턴스)는 Eden 영역에 위치하게 된다. Eden 영역에서 GC가 한번 발생한 후 살아남은 객체는 Survivor 영역중 하나로 이동한다. 이과정을 반복하다가 계속해서 살아남아 있는 객체는 일정시간 참조되고 있다는 뜻으로 Old 영역으로 이동시킨다.

    Major GC :
    Old 영역에 있는 모든 객체들을 검사하여 참조되지 않은 객체들을 한꺼번에 삭제한다. 이 작업은 시간이 오래걸리고 실행중인 프로세스가 정지된다 이를 'stop the world' 라고 하는데 Major GC 가 발생하면 GC 를 실행하는 쓰레드를 제외한 모든 쓰레드의 작업은 멈춘다. Major GC 가 작업을 완료한 이후에 중단했던 쓰레드를 실행한다.


JAVA 의 Garbage Collection

JAVA 의 Garbage Collection

JVM의 구조에 대해서 포스팅하며 Heap 영역에서 동작하는 GC 에 대해서 언급을 하였다. JVM의 구조와 자바의 메모리 구조 JVM의 구조와 자바의 메모리 구조 자바 애플리케이션이 실행될 때 JVM에서 일

dev-cool.tistory.com


참조 :
https://d2.naver.com/helloworld/329631