JVM

이재용·2025년 4월 6일
0

OS에 종속받지 않고 CPU가 Java를 인식, 실행할 수 있게 하는 가상 컴퓨터

Java 소스코드->Java bytecode->binary code(기계어)
Java Compiler가 .java파일을 .class라는 Java bytecode로 변환한다.
그리고 JVM이 OS가 bytecode를 이해할 수 있도록 해석해준다. 따라서 bytecode는 JVM 위에서 OS 상관없이 실행될 수 있다.
bytecode는 다시 JIT 컴파일러에 의해 바이너리 코드로 변환된다.

Java Compiler는 JDK를 설치하면 javac.exe라는 실행 파일 형태로 설치된다. 정확히는 JDK 의 bin폴더에 javac.exe로 존재한다.

javac 명령어

java파일을 class파일로 컴파일
Java Complier의 javac 명령어를 사용

javac test.java  //test.class 생성

java 명령어

class파일을 JVM으로 실행
JDK 디렉토리의 /bin 폴더에 존재하는 java.exe 는 JVM을 구동시키기 위한 명령 프로그램이다. (.JRE) java 명령어로 JVM을 실행시킬 수 있다.

java test  //.class는 붙이지 않음
java -jar 파일명.jar  //cd를 한 상태
java -jar 파일위치/파일명.jar  //cd하지 않고

JVM의 구성요소

  • Class Loader
  • Runtime Data Area
    - Method Area
    - Heap
    - Stack
    - PC Register
    - Native Method Stack
  • Execution Engine
    - Interpreter
    - JIT Compiler
    - Garbage Collector

1. Class Loader

Byte Code(.class 파일)를 Runtime Data Area로 로딩.

2. Runtime Data Area

프로그램을 수행하기 위해 OS에서 할당받은 메모리 공간
![[Runtime Data Area.png|700]]

2.1 Method Area(Static Area)

Byte Code, static 변수
모든 스레드가 공유한다.

2.2 Heap

Reference Type, 동적으로(new 연산자로) 생성된 객체
모든 스레드가 공유한다.

2.3 Stack

Primitive Type, Heap영역에 있는 객체를 가리키는 참조 변수, 지역변수, 매개변수, return 값
메서드가 호출될 때 마다 새로운 스택프레임이 생성된다.
Thread는 자기만의 stack을 가진다.

2.4 PC Register

Thread마다 하나씩 존재한다. 현재 실행되고 있는 명령어의 주소가 저장된다.

2.5 Native Method Stack

다른 언어로 작성된 메서드를 실행할 떄 사용된다.

3. Execution Engine

클래스를 실행시키는 역할이다.
클래스 로더가 JVM내의 런타임 데이터 영역에 바이트 코드를 배치시키고, 이것은 실행 엔진에 의해 실행된다.

3.1 JIT Compiler

인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드 전체를 컴파일하여 기계어로 변경하고, 이후에는 해당 더 이상 인터프리팅 하지 않고 기계어로 직접 실행하는 방식이다.

3.2 Garbage Collector

동적으로 할당한 메모리 영역 중 사용하지 않는 영역을 탐지하여 해제하는 기능.

Heap

동적으로 생성된 객체가 저장되는 공간

Young Generation

  • Eden : 새롭게 생성된 자바 객체가 할당되는 메모리 영역

  • Survivor0 : Minor GC시에 Eden에서 살아남은 객체가 오는 곳

  • Survivor1 : Minor GC시에 Survivor1에서 살아남은 객체가 오는 곳

    Minor GC
    Eden 영역이 꽉 차면 Minor GC가 발생하게 된다. 참조되지 않는 메모리는 해제되고 Eden 영역에 존재하는 사용중인 객체는 Survivor 영역으로 옮겨지게 된다.

Old Generation

Young Generation에서 살아남은 객체들이 오는 곳
Major GC(=Full GC) - 실행 시 오버헤드(지연)가 생긴다.

참고 - Java 8 JVM

<----- Java Heap -----> <--------- Native Memory ---------->
+------+----+----+-----+-----------+--------+--------------+
| Eden | S0 | S1 | Old | Metaspace | C Heap | Thread Stack |
+------+----+----+-----+-----------+--------+--------------+

MetaSpace

  • 클래스, 메소드의 메타데이터를 저장
  • 애플리케이션의 필요에 따라 동적으로 크기를 조정할 수 있다.
  • 리플렉션 클래스 로드 시 사용(Spring)<-Spring이 MetaSpace를 사용함
    - Spring이 MetaSpace를 얼마나 사용할 지 계산하는 방법 -> 단위시간 당 Request와 그로 인해 발생하는 TPS(Transaction Per Second)
  • Java 8에서 도입. 기존의 Permanent Generation 공간을 대체한다.

동작 방식

Mark And Sweep

루트에서부터 해당 객체에 접근 가능한지, 아닌지를 해제의 기준으로 삼는다.
1. 루트부터 그래프 순회를 통해 연결된 객체들을 찾아내고 (Mark)
2. 연결이 끊어진 객체들은 지우는 방식이다. (Sweep)
3. Sweep 이후에는 분산되어 있던 메모리를 정리하여 메모리 파편화를 막는다. (Compaction)
다만, Mark And Sweep에서 Compaction은 필수는 아니다.
루트로부터 연결된 객체는 Reachable, 연결되지 않았다면 Unreachable이라고 부른다.

장점

Mark And Sweep 방식을 사용하면, 루트로부터 연결이 끊긴 순환 참조되는 객체들도 지울 수 있다.

단점

객체의 reference count가 0이 되면 지워버리는 reference counting 방식과는 달리, Mark And Sweep은 의도적으로 특정 순간에 GC를 실행시켜야 한다.
즉 어느 순간에는 실행 중인 어플리케이션이 GC에게 컴퓨터 리소스들을 내줘야 한다.

Root Space

JVM에서 Root Space는 Heap 영역 메모리에 대한 참조를 들고 있을 수 있는 영역을 뜻한다.
1. Stack의 로컬 변수
2. Method Area의 Static 변수
3. Native Method Stack의 JNI 참조

0개의 댓글