본문 바로가기
Delvelopment/Java

[JAVA] JVM의 구성요소 및 동작원리

by 제제킴 2021. 12. 26.
반응형

 

JVM이란? Java Virtual Machine?

JVM(Java Virtual Machine)은 몇가지 특징을 가지고 있다.

  • 플랫폼(windows, Mac OS, Linux, etc)에 독립적이다. OS에 구애받지 않고 사용이 가능하다.
  • 프로그램이 실행되는 도중에도 동적으로 OS로부터 메모리 할당받아 스스로 관리한다 (GC, Garbage Collection)

바이트 코드란

바이트 코드란 JVM이 이해할 수 있는 언어이다.Java는 OS에 독립적이기 때문에 JVM이 이해할 수 있는 언어로 제공해야하며, Java 컴파일러를 통해 바이트코드로 컴파일한다.

JVM의 구성요소 및 동작

JVM의 구성요소는 Memory 관리, Class Loader, Execution Engine 등으로 구성되어 있다.

 

JVM의 동작

하나의 java파일이 JVM을 통해 실행은 아래처럼 이루어진다.

  1. 프로그램이 실행되면 OS로 부터 프로그램이 필요로 하는 메모리를 먼저 할당 받는다.
  2. 자바 컴파일러를 통해 개발자가 작성한 코드 (.java)를 바이트 코드(.class)로 변환한다.
  3. 다음 Class Loader에서 바이트 코드 JVM에 로딩 시킨다.
  4. 로딩 된 바이트 코드를 execution engine을 통해 기계어로 해석된다.
  5. 해석된 바이트 코드들은 reuntime data areas에 배치되어 실질적인 수행이 이루어진다.

더 자세하게 JVM 구조를 살펴보면.

  • Class Loader
    • 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는 더이상 사용하지 않는 인스턴스들을 삭제하는 역할을 수행한다.
      • 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메모리는 절대 마음대로 조정해선 안된다.

 

JIT (Just In Time)

JIT이란 JIT 컴파일러가 런타임에 Java의 성능을 향상시키는 Java Runtime Environment의 구성 요소이다.
인터프리터 방식의 단점을 보완하기 위해 도입되었으며, 실행 시점에 인터프리터 방식으로 기계어 코드를 생성하면서 그것을 캐싱합니다. 캐싱을 통해 필요할때마다 사용하여 빠르게 처리한다.
추가로, JAVA는 바이트코드(.class)로 컴파일 하는 과정과, 바이트코드를 인터프리터 하는 과정을 2번을 진행하기 때문에 컴파일속도가 느린편이다.

  • 컴파일 방식
    • 소스코드를 한 번에 컴퓨터가 읽을 수 있도록 기계어로 변환
  • 인터프리터 방식
    • 소스코드를 빌드시에 아무것도 하지 않다가, 런타임시에 한줄 씩 변환.

Java는 이 두가지 방식을 모두 사용하고 있습니다.
JVM의 종류에 따라 동작 방식이 나뉠 수 있다. (오라클, IBM, etc)

 

JDK와 JRE의 차이.

JRE(Java Rentime Environment)는 Java 애플리케이션을 생성하고 실행하기 위한 일련의 구성요소이다.
JDK(Java Development Kit) 는 JRE + 개발에 필요한 것들을 가지고 있는 더 큰 범위라고 보면된다.
즉, JDK는 Java 프로그램 개발과 실행을 할 수 있는 환경을 제공하며, JRE는 Java프로그램을 실행하는 환경을 제공한다.

반응형

댓글