01. JVM

Kim Sang Yeob·2022년 12월 28일

Study Java

목록 보기
1/3
post-thumbnail

학습할것

  • JVM이란 무엇인가
  • JVM 구성 요소
  • Java Compiler
  • ByteCode란 무엇인가
  • JIT 컴파일러란 무엇인가
  • JDK와 JRE의 차이

JVM이란 무엇인가?

JAVA Virtial Machine의 약자로 Java Byte Code를 OS에 맞게 해석해주는 역할을 하는 가상머신

  • Java Compiler는 .java파일을 .class라는 Java ByteCode로 변환
    -> ByteCode는 기계어가 아니기 때문에 OS에서 바로 실행이 안됨
  • JVM은 OS가 ByteCode를 이해할 수 있도록 해석해 주는 역할을 함
    -> 최초의 JVM은 Interpreter로 c언어 같은 네이티브 언어에 비해 속도가 느렸지만, JIT(Just In Time) 컴파일러 구현을 통해 극복
  • ByteCode는 JVM위에서 OS상관없이 실행된다.
    -> JVM은 OS에 독립적이지만 의존적이다.

JVM 구성 요소

01.JVM-JVM의 구성.png

  • Class Loader
    • Runtime 시점에 .class에서 ByteCode를 읽고 메모리에 저장
    • 로딩 : 클래스를 읽어오는 과정
    • 링크 : 레퍼런스를 연결하는 과정
    • 초기화 : static 값들을 초기화 및 변수에 할당
  • Runtime Data Areas
    • Heap과 Method는 모든 쓰레드가 공유 / 나머지는 쓰레드마다 생성
    • JVM이 프로그램을 수행하기 위해 OS로부터 별도로 할당받은 메모리 공간 / 크게 5가지 영역으로 나눌 수 있다.
      • PC Register : CPU가 Instruction을 수행하는 동안 필요한 정보를 저장
      • JVM Stack : Thread가 시작될 때 생성되면 Method와 Method 정보 저장
      • Navtive Method Stack : Java 이외의 언어로 작성된 native 코드를 위한 Stack(JNI)
      • Method Area : 모든 쓰레드가 공유하는 메모리 영역(클래스, 인터페이스, 메소드, 필드, Static 변수 등의 ByteCode 등을 보관)
      • Heap : 런타임시 동적으로 할당하여 사용하는 영역 클래스를 통해 instance를 생성하면 Heap에 저장됨
      • Heap의 경우 명시적으로 만든 class와 암묵적인 static 클래스(.class 파일의 class)가 담긴다.
      • 또한 암묵적인 static 클래스의 경우 클래스 로딩시 class 타입의 인스턴스를 만들어 힙에 저장한다. 이는 Reflection에 등장한다.
  • Execution Engine
    • Load된 Class의 ByteCode를 실행하는 Runtime Module
    • Class Loader를 통해 JVM 내의 Runtime Data Areas에 배치된 ByteCode는 Execution Engine에 의해 실행(ByteCode를 명령어 단위로 읽어서 실행)
  • Garbage Collector
    • Heap 메모리 영역에 생성된 객체들 중에 참조되지 않은 객체들을 제거하는 역할을 한다.
    • GC의 동작시간은 일정하게 정해져 있지 않기 때문에 언제 객체를 정리할지는 알 수 없다. 즉, 바로 참조가 없어지자마자 작동하는 것이 아니라는 것이다.
    • 또한 GC를 수행하는 동안 GC Thread를 제외한 다른 모든 Thread는 일시정지상태가 된다.

Java Compiler

흔히 우리가 JIT Compiler라고 부르는 컴파일러는 실행중에 ByteCode를 다양한 테크닉을 사영하여 JVM 해석 엔진 없이 바로 수행되는 기계어 코드를 만들어낸다. 따라서, ByteCode가 가지는 장점과 기계어가 가지는 장점을 결합할 수 있다.

과정

  1. Java Compiler(javac 명령어 실행)에 의해 Java Source(.java 확장자)로부터 ByteCode(.class 확장자)가 생성된다.
  2. JVM에 있는 Class Loader에 의해 ByteCode는 JV M내로 로드되고 실행엔진에 의해 기계어로 해석되어 메모리 상(Runtime Data Area)에 배치된다.
  3. 실행엔진에는 Interpreter와 JIT(Just In Time) Compiler가 있는데, Interpreter에 의해 ByteCode를 한 줄씩 읽어 실행하다가 적절한 시점에 ByteCode 전체를 컴파일하여 인터프리팅을 멈추고 해당 코드를 직접 실행한다.
  • JIT Compiler에 의해 해석된 코드는 캐시에 보관하기 때문에 한 번 컴파일된 후에는 빠르게 수행할 수 있다는 장점이 있다. 하지만 코드 전체를 컴파일하기 때문에 Interpreter보다 시간이 오래 걸리므로 한 번만 실행해도 되는 코드에 대해서는 Interpreter가 유리하다.
  • Interpreter : 자바 ByteCode를 한 줄씩 실행. 전체 성능면에서 불리
  • JIT Compiler : 전체 ByteCode를 컴파일하고 캐시에 보관해서 직접실행. 한 번만 실행해도 되는 코드에 대해서는 Interpreter가 유리.

장점

  • 생성되는 코드의 안정성
    • Java가 수행중 만들어내는 기계어 코드는 안전한 공간(sandBox)안에서 돌아가기 때문에 외부 해킹에 안전
  • 동작하는 메모리 공간의 안전성
    • 모든 자바 객체들은 Heap이라는 독립적인 공간에서만 수행
    • 다른 Process와 다른 메모리 공간을 사용하기 때문에 Stack Overflow에 강함
  • 최적화 재사용에 유일한 관련 클래스간 상속구조
    • 메모리 위치상 가깝게 관련된 객체와 메소드들을 위치시킨다.
    • method inlining 같은 성능을 높이기 위한 테크닉들이 자바에서 효율적으로 작동
  • 동적 최적화와 그것에 대한 취소, 재 최적화 가능
    • static 언어와 다르게 dynamic class loading으로 어떤 방식으로든 수행중 변경 가능 compiler를 통한 최적화가 수시로 이루어진다.

Compiler 기술들

  • Hot Spot Detection
    • JVM이 ByteCode를 해석하다가 루프등을 만나 몇번이나 중복적인 해석이 이루어진다고 판단되면 Byte코드를 기계어로 직접 컴파일하는 방식
    • 기존의 모든 기본 코드를 수행전에 컴파일 하는 방식은 수행 자체는 빠르지만, 프로그램의 크기가 커지고 기기별 이식성이 떨어지기도 한다.
  • Method inlining
    • 클래스 안에서 사용된 다른 클래스에 대해 method inlining을 수행함으로서 다른 메모리 공간에 있는 메소드에 대해 호출하는 것을 피할 수 있다.
    • 이걸 취소할 수도 있다.
  • reflection
    • 객체를 명시적으로 코드에서 new하지 않아도 임의의 객체를 동적으로 생성하고 메소드를 호출할 수 있는 reflection은 자바 동적 클래스 로딩의 핵심

ByteCode란 무엇인가?

바이트코드(Bytecode, portable code, p-code)는 특정 하드웨어가 아닌 가상 컴퓨터에서 돌아가는 실행 프로그램을 위한 이진 표현법이다. 하드웨어가 아닌 소프트웨어에 의해 처리되기 때문에 보통 기계어보다 더 추상적이다.

JVM이 사용자가 작성한 .java 소스 코드 파일을 운영체제에 실행 가능한 명령어 집합 파일로 컴파일하는 과정 중에서 필요한 코드

  • JVM이 이해할 수 있는 언어로 변환된 자바 소스코드를 의미
  • 자바 컴파일러에 의해 변환되는 코드의 명령어의 크기가 1byte라서 자바 바이트 코드라고 불림
  • 자바 바이트 코드는 자바 가상 머신만 설치되어 있다면 어느 운영체제에서도 실행 가능

JIT Compiler란 무엇인가?

  • JIT 컴파일러는 Just In Time 컴파일러로 바이트코드를 기계어로 번역하여 실행하는 것을 의미한다.
  • JIT는 bytecode를 어셈블리 같은 nativecode로 바꿔서 실행이 빠르지만 역시 변환하는데 비용이 발생한다. 이 같은 이유 때문에 JVM은 모든 코드를 JIT compiler 방식으로 실행하지 않고 interpreter 방식을 사용하다 일정 기준이 넘어가면 JIT compiler 방식으로 실행한다.
  • Interpreter 방식은 바이트코드를 한 줄 씩 읽으면서 코드를 실행하기 때문에 동일한 메소드를 실행하는 경우 중복해서 번역하는 비효율이 있다. 이를 방지해서 보다 좋은 성능을 낼 수 있게 하기 위해 JIT Complier는 번역한 내용을 캐싱해 두었다가 동일한 메소드를 실행할 경우 다시 번역하지 않고 캐싱된 내용을 실한다.

JDK와 JRE의 차이

  • JRE : Java Runtime Environment
    • JRE는 JVM과 Class loader로만 이루어진 자바를 실행하기 위한 프로그램
  • JDK : Java Development Kit
    • JDK는 JRE + JRE에 없는 자바 컴파일러(javac, java compiler) 등

참조

profile
Studying NodeJS...

0개의 댓글