본문으로 바로가기

JVM(Java Virtual Machine)에 대하여

category Java 2021. 1. 23. 19:32

자바 바이트코드를 OS에 맞게 해석해주는 역할을 하는 가상머신을 JVM이라고 부른다.

한정된 메모리를 효율적으로 사용하여 최고의 성능을 내기 위해 JVM을 꼭 알아야 한다. 동일한 기능을 하는 프로그램이라도 메모리 관리에 따라 성능이 좌우된다. 메모리 관리가 되지 않은 경우 속도저하 현상이나 튕김 현상 등이 일어나게 된다.

자바 실행 과정

사진 출처 : http://tcpschool.com/

  1. 프로그램이 실행되면 JVM은 OS로 부터 이 프로그램이 필요로 하는 메모리를 할당받는다.
  2. 자바 컴파일러(javac)가 자바 코드(.java)를 읽어들여 자바 바이트코드(.class)로 변환한다.
  3. Class Loader를 통해 class 파일들을 JVM으로 로딩한다.
  4. 로딩된 class 파일들은 실행엔진(Execution engine)을 통해 해석된다.
  5. 해석된 바이트 코드는 Runtime Data Areas에 배치되어 실질적으로 수행한다.

JVM 구조

사진 출처 : 더 자바 코드를 조작하는 여러가지 방법 인프런 강의 (백기선)

클래스 로더 시스템 (Class Loader)

JVM으로 .class파일을 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈이다. 자바는 Runtime시에 동적으로 클래스를 로드(=로딩)한다. (클래스를 처음으로 참조할 때, 해당 클래스를 로드하고 링크한다는 것이다.)

쉽게말해서 자바 바이트코드를 읽어들여서 메모리에 적절하게 배치하는게 클래스로 로더가 하는일이다.

클래스 로더는 크게 로딩,링크,초기화 세 가지로 나뉜다.

  • 로딩 : 클래스를 읽어오는 과정
  • 링크 : 객체를 연결하는 과정
  • 초기화: static 값들 초기화 및 변수에 할당

실행 엔진(Execution Engine)

클래스를 실행시키는 역할을 한다. 클래스 로더가 바이트 코드를 메모리에 배치시키는 역할을 한다면 실행 엔진은 이것을 실행시킨다.

자바 컴파일러를 통해 변환된 바이트 코드는 기계가 바로 실행할 수 있는 언어가 아닌 인간이 이해하기 편한 언어에 가깝기 때문에 아래 두 가지 방식을 통해 기계가 이해할 수 있는 언어로 변환해준다.

(앞으로는 인간이 이해하기 편한 언어를 네이티브 코드로 표현하겠다.)

  • Interpreter(인터프리터)

    실행 엔진은 자바 바이트 코드를 한줄씩 읽어서 기계가 이해할 수 있는 언어로 변환한다.

    -> 한줄씩 수행하기 때문에 속도가 매우 느리다.

  • JIT(Just In Time)

    인터프리터의 효율을 높이기 위해, 인터프리터가 반복되는 코드를 발견하면 JIT 컴파일러로 반복되는 코드를 모두 기계가 이해할 수 있는 언어인 네이티브 코드로 바꿔둔다. 그 다음부터 인터프리터는 네이티브코드로 컴파일된 코드를 바로 사용한다. 네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 빠르게 수행될 수 있다.

    JIT 컴파일러가 컴파일 하는 과정은 바이트 코드를 인터프리팅하는 것보다 훨씬 오래걸리므로 한번만 실행하는 코드라면 인터프리팅 하는 것이 유리하다.

    -> 이 과정은 JVM이 내부적으로 해당 메서드의 실행 빈도를 체크하여 판단한다.

  • GC(Garbage Collector)

    더이상 참조되지 않는 객체를 모아서 정리한다.


메모리(Runtime Data Area) : 프로그램을 수행하기 위해 OS에서 할당받은 메모리 공간

사진 출처 : https://asfirstalways.tistory.com/

각 Thread마다 존재하는 메모리 영역

PC Register

Thread가 시작될 때 생성되며, 각 Thread별로 고유의 독립적인 공간을 가진다. Thread가 어떤 부분을 어떤 명령으로 실행해야 할 지에 대한 기록을 하는 부분으로 현재 수행중인 JVM명령의 주소를 갖는다.

Stack (스택 영역)

프로그램 실행 과정에서 임시로 할당되었다가 메소드 호출이 종료됨과 동시에 소멸되는 데이터를 저장하기 위한 영역이다.

메소드가 호출될 때 마다 메모리 안에 그 메서드만을 위한 공간인 스택 프레임이 생성되며, 그 안에는 메소드의 지역변수와 파라미터 변수가 포함되어 있다.

메서드의 수행이 끝나면 스택 프레임 별로 삭제된다.

Native method stack

.class 가 아닌 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역으로, Java로 작성된 코드가 아닌 C 또는 C++로 작성된 코드들의 공간이다.

JNI(Java Native Interace)를 통해 바이트 코드로 전환하여 저장하게 된다. JNI는 아래 코드처럼 native 키워드가 붙은 메소드를 호출한다.

@HotSpotIntrinsicCandidate
public static native Thread currentThread();

모든 Thread가 공유하는 영역

Method Area(메소드 영역)

메소드 영역은 다른말로 클래스 영역 또는 스태틱 영역이라고도 불린다.

클래스 수준의 정보(클래스 이름, 부모 클래스 이름, 메소드, 변수등)가 저장된다.

클래스 정보를 처음 메모리 공간에 올릴 때 초기화 되는 대상을 저장하기 위한 메모리 공간이다. 대부분의 인스턴스 생성이 메소드 내에서 명령하고 호출하기 때문에 사실상 컴파일 된 바이트 코드의 대부분이 메소드 바이트코드이기 때문에 대부분의 바이트 코드가 모두 이 공간에 올라간다고 봐도 상관 없다.

이 공간에는 추가적으로 Runtime Constant Pool 이라는 별도의 영역이 존재하는데, 상수 자료형을 저장하여 참조하고 중복을 막아 효율적인 메모리를 관리하는 역할을 한다.

메소드 영역은 GC의 관리 대상이다.

heap(힙 영역)

객체를 저장하는 가상 메모리 공간으로, new키워드로 생성된 객체와 배열을 저장하며 메소드 영역에 올라온 클래스들만 객체로 생성 가능하다.

힙 영역은 다음과 같이 세 영역으로 나뉜다.

1. Permanent Generation

생성된 객체들의 정보의 주소값이 저장된 공간이다. Class Loader에 의해 load되는 클래스와 메소드 등에 대한 Meta정보다 저장되는 영역이며, Reflection을 이용하여 동적으로 클래스가 로딩되는 경우에도 사용된다.

내부적으로 Reflection 기능을 사용하는 스프링 프레임워크를 이용할 경우, 이 영역에 대한 고려가 필요하다.

Permanent Generation영역은 Java8부터 제거되고 Metaspace영역으로 전환되었다.

그 이유는 아래와 같다.

https://johngrib.github.io/wiki/java8-why-permgen-removed/

즉, 각종 메타 정보를 OS가 관리하는 영역으로 옮겨 Perm 영역의 사이즈 제한을 없앤 것이라 할 수 있다.

2. New/Young Generation

  • Eden: 객체들이 최초로 생성되는 공간
  • Survivor 0/1: Eden에서 참조되는 객체들이 저장되는 공간
  • Eden 영역이 가득차면 첫 번째 GC가 발생하게 된다. Eden영역에 있는 값들을 survivor 영역 중 한 곳에 복사하고 나머지 영역에 있는 객체들을 삭제한다. -> Minor GC

3. Tenured Generation

  • New/Young Generation에서 일정 시간 이상 참조되고 있는 객체들이 저장되는 공간, survivor 영역에 계속해서 살아남는 객체들을 이곳으로 옮긴다. 모든 객체들을 검사하여 참조되지 않은 객체를 전부 삭제한다. 시간이 오래걸리고 GC를 실행하는 스레드를 제외하고는 전부 작업을 멈추고 GC가 완료되면 다시 실행된다. stop-the-world 라고도 한다. -> Major GC

인스턴스는 소멸 방법과 소멸 시점을 파악할 수 없기 때문에 heap 영역에 할당된다. 더 이상 참조되지 않고 존재할 이유가 사라진 인스턴스들은 GC에 의해 소멸된다.

추가적으로 heap 영역은 모든 Thread가 공유하고 있는 영역이기 때문에 멀티 스레드 환경에서의 동시성 문제는 모두 heap 영역에서 발생하게 된다.


Reference

https://asfirstalways.tistory.com/158

https://annajinee.tistory.com/42

https://dundung.tistory.com/228