JVM이란? #2 JVM 구성 요소와 역할 정리

최동민·2022년 11월 14일
0
post-thumbnail

왜 자바 가상머신 (JVM)을 알아야 하는가?

한정된 메모리를 효율적으로 사용하여 최고의 성능을 내기 위해서이다. 메모리 효율성을 위해 메모리 구조를 알아야 하기 때문이다. 동일한 기능의 프로그램이더라도 메모리 관리에 따라 성능이 좌우된다. 메모리 관리가 되지 않은 경우 속도저하 현상이나 튕김 현상 등이 일어날 수 있다. 그래서, 알아두면 좋다를 넘어서 알아야 하는 것이다.

(JVM은 스택 기반의 VM(가상머신)이다. )

자바 프로그램 실행과정

  1. 프로그램이 실행되면 JVM은 OS로부터 이 프로그램이 필요로 하는 메모리를 할당받는다. JVM은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.
  2. 자바 컴파일러(javac)가 자바 소스코드(.java)(개발자가 작성한 코드)를 읽어들여 자바 바이트코드(.class)로 변환시킨다.
  3. Class Loader를 통해 class파일들을 JVM으로 로딩한다.
  4. 로딩된 class파일들은 Execution engine을 통해 기계어로 해석된다.
  5. 해석된 바이트코드는 Runtime Data Areas 에 배치되어 실질적인 수행이 이루어지게 된다. 이러한 실행과정 속에서 JVM은 필요에 따라 Thread Synchronization과 GC같은 관리작업을 수행한다.

JVM 구조

JVM은 크게 아래 4가지로 구성된다.

  • 자바 인터프리터(interpreter)
  • 클래스 로더(class loader)
  • JIT 컴파일러(Just-In Time compiler)
  • 가비지 컬렉터(garbage collector)

이 전 포스팅에서도 설명했듯이 자바 컴파일러는 자바 언어로 작성한 코드를 JVM이 이해할 수 있는 자바 바이트 코드로 변환한다. 우리가 흔히 아는 javac.exe 실행 파일이 바로 자바 컴파일러 실행 파일이다.

자바 컴파일러가 바이트 코드로 변환하는 과정을 거치면 JVM 내부의 클래스 로더로 들어오게 된다.

클래스 로더(class loader)

자바는 동적으로 클래스를 읽어오기 때문에 프로그램이 실행중인 런타임에서 JVM과 연결된다.

Class Loader는 로딩 → 링킹(linking) (래퍼런스 연결) → 초기화(Initialization) 세가지 기능을 동작한다.
클래스 로더는 자바 컴파일러를 통해 바이트 코드(.class)로 변환된 바이트 코드를 JVM에 로딩, 링킹, 초기화 시키는 역할을 한다.
Method Area에 적재. 로딩이 끝나면 Heap Area에 적재.
Linking은 검증(Verify)을 통해 .class 파일 형식이 유효한지 체크와 (Preparation) 클래스 변수(static)의 필요한 메모리, (Resolve) 메모리 래퍼런스를 Method 영역에 있는 실제 래퍼런스로 교체한다.
Initailization, Static 변수의 값을 할당한다.

한 번에 메모리에 모든 클래스를 로드하는 것이 아닌, 필요한 순간에 해당 클래스(.class) 파일을 찾아 메모리에 로딩해주는 역할을 하는 것이 바로 클래스 로더(class loader) 이다.

클래스 로더는 계층 구조로 이루어져 있으며 세가지 클래스 로더가 제공된다.

  • Bootstrap Class Loader : JAVA_HOME/lib에 있는 핵심 자바 API를 제공한다. 다른 모든 ClassLoader 인스턴스의 부모 역할을 합니다.
  • Extension Class Loader : JAVA_HOME/lib/ext 폴더 또는 java.ext.dirs 시스템 변수에 해당하는 위치에 있는 클래스를 읽는다. 부트 스트랩 클래스 로더의 자식이고 모든 응용 프로그램에 사용할 수 있도록 표준 핵심 자바 클래스(standard core java classes)의 확장을 로딩합니다
  • System Class Loader : Application class loader로도 불립니다. application level의 클래스들을 로드합니다

Memory 관리

  • Method Area는 field, method, tpye constant pool, static, final 등이 관리된다.
  • Heap Area는 모든 객체와 인스턴스 변수, 배열 등이 저장된다.
  • Statck Area는 지역 변수와 thread가 관리된다.
  • PC register는 운영체제에서 관리하는 context switch가 발생할때 사용한다.
  • Native Method Stack은 자바가 아닌 다른 언어로 작성된 코드를 실행할때 사용한다.

Execution engine

  • 로드된 바이트코드(.class)를 실행하는 엔진이다. 바이트코드를 명령어 단위로 읽어서 실행하는데 인터프리터 방식과 JIT 방식을 사용한다. JIT으로 중복되는 부분을 미리 체크하고 라인별로 인터프리터 방식으로 실핸한다. 체크한 부분에 도달하면 미리 캐시된 값을 실행하는 방식으로 진행한다.
  • GC(Garbage Collection)도 존재한다. GC는 더이상 사용하지 않는 인스턴스들을 삭제하는 역할을 수행한다.

가비지 컬렉터(garbage collector)

  • GC는 힙 영역에서 사용하지 않는 객체들을 제거하는 작업을 총칭한다. 이 객체를 제거하는 작업이 필요한 이유는 자바는 개발자가 메모리를 직접 해제해줄 수 없는 언어이기 때문이다. 따라서 객체를 사용하고 제거하는 기능이 필요하게 된다
  • GC의 동작방식은 가장 간단한 SerialGC방식으로 설명한다. GC는 Minor GC, Major GC로 구분할 수 있다. Minor GC는 young영역에서 , Major GC는 old영역에서 일어난다고 정의한다. GC를 수행할때는 GC를 수행하는 스레드 이외의 스레드는 모두 정지한다. 이를 Stop-the-world 라고 한다. Minor GC는 Eden영역이 가득 참에서 부터 시작된다. Eden영역에서 참조가 남아있는 객체를 mark하고 survivor 영역으로 복사한다. 그리고 Eden영역을 비운다. Suvivor영역도 가득차면 같은 방식으로 다른 Suvivor영역에 복사하고 비운다. 이를 반복하다 보면 계속해서 살아남는 객체는 old영역으로 이동하게 된다. Major GC는 old 영역에서 일어난다. 위와 반대로 삭제되어야 하는 객체를 mark한다. 그리고 지운다. 메모리는 단편화 된 상태이므로 이를 한 군데 모아주는 것을 Compaction이라 하며 Compat 라한다. 그래서 Mark-Sweep-Compact 알고리즘 이라고 한다. 이것이 중요한 이유는 GC 수행시 시스템이 멈추기 때문에 의도치 않은 장애의 원인이 될 수 있다. 따라서 이를 위해 힙영역을 조정하는 것을 GC 튜닝이라고 하고 JVM 메모리는 절대 마음대로 조정해선 안된다.
profile
코드를 두드리면 문이 열린다

0개의 댓글