ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 네이티브 머신 코드,HotSpot JVM , jvm 메모리구조
    자바웹프로그래밍/JAVA 2025. 9. 5. 10:20
    728x90
    반응형

    네이티브 머신 코드

     

    jvm 성능 공부 및 postgresql에서 쿼리 최적화를 하기 위해서 공부를 하고있는데

     

    성능이 좋아지기위해 자주 사용 하는 코드를 네이티브 머신코드로 변경하다는 말을 자주 본다.

     

    해당 네이티브 머신코드는 cpu가 직접 이해할수있는 이진 코드이며 (11111111 10101010) 같은 코드라고 보면된다.

     

     

     

    HotSpot JVM

    HotSpot JVM은 단순히 통계만 모으는 게 아니라 → 그 통계를 바탕으로 “어떤 코드를 JIT 컴파일할지, 어떤 최적화를 적용할지”까지 결정하는 엔진

     

    2. HotSpot JVM의 실행 과정

    1. 처음 실행 시 (Interpreter)
      • 자바 바이트코드(.class)는 바로 CPU에서 실행할 수 없으니, 처음에는 인터프리터가 한 줄 한 줄 해석하면서 실행합니다.
      • 이 단계는 빠르게 시작할 수 있지만, 성능은 상대적으로 느립니다.
    2. 프로파일링
      • JVM은 코드를 실행하면서 “어떤 메소드가 많이 호출되는지”, “루프가 몇 번 돌았는지” 같은 **실행 통계(프로파일링)**를 모읍니다.
      • 예: for (int i=0; i<1_000_000; i++) 같은 루프는 실행 횟수가 많으니 “뜨거운 코드”로 분류됩니다.
    3. JIT 컴파일 (C1, C2 컴파일러)
      • C1 (Client Compiler): 빠른 최적화, 주로 GUI 애플리케이션이나 짧게 실행되는 프로그램에서 유용
      • C2 (Server Compiler): 더 깊고 강력한 최적화, 실행 시간이 긴 서버 애플리케이션에서 효과적
      • HotSpot JVM은 Tiered Compilation을 써서 → 처음에는 C1으로 JIT → 코드가 진짜 “핫”하면 C2로 다시 컴파일해 성능을 최대한 끌어올립니다.

     

    🧩 배경지식: JVM의 메모리 모델

    자바는 “Write Once, Run Anywhere” 를 목표로 설계되어, 운영체제나 CPU 아키텍처에 상관없이 동작해야 합니다.
    이를 위해 JVM은 실행 시 자체 메모리 구조(Runtime Data Areas)를 만들고, 이 위에서 객체 관리·스레드 실행·JIT 최적화 등을 수행합니다.


    1. 런타임 데이터 영역 (Runtime Data Areas)

    1-1. Heap (가장 중요)

    자바 객체(instance) 대부분이 생성되는 공간. GC(Garbage Collector)의 주요 관리 대상입니다.

    • Young Generation
      • Eden: 새 객체가 처음 생성되는 공간.
      • Survivor 0 / 1 (S0, S1): GC 이후 살아남은 객체가 잠시 머무르는 공간.
      • 특징: 객체 대부분은 금방 사라지므로, Young GC는 매우 자주 실행됨(Stop-the-World).
    • Old Generation (= Tenured)
      • Young GC를 여러 번 거치고 살아남은 “장수 객체”가 승격(Promotion)되어 들어오는 공간.
      • 크기가 커서 GC 비용도 큼 → 주로 Full GC가 대상.
    • Region 기반 관리 (G1, ZGC, Shenandoah)
      • 예전에는 Heap을 큰 단일 블록으로 관리했지만, G1/ZGC는 수 MB 크기의 작은 Region 단위로 나눔.
      • 장점: 단편화 완화, 병렬/동시 GC 가능, 멀티코어 활용 ↑.

    1-2. Metaspace

    • Java 8 이전: PermGen (Permanent Generation) → 크기 제한이 있었음.
    • Java 8 이후: Metaspace로 대체, 네이티브 메모리를 사용.
    • 저장되는 것: 클래스 메타데이터 (클래스 구조, 메소드/필드 정보, 상속 관계 등).
    • PermGen OOM 문제를 해결했지만, 여전히 무한 확장은 아님(기본적으로 OS 메모리에 제한됨).

    1-3. Thread-Local 영역

    JVM에서 실행되는 각 스레드마다 독립적으로 유지되는 영역.

    • Java Stack
      • 메소드 호출 시마다 생성되는 Stack Frame들이 쌓임.
      • Frame = 로컬 변수 배열 + Operand Stack(연산 스택) + 상위 호출자 정보.
      • 원시 타입(int, long 등)과 참조 변수(객체 포인터)가 저장됨.
    • PC Register
      • 현재 실행 중인 바이트코드 명령의 주소를 가리킴.
    • Native Stack
      • C/C++ 네이티브 메소드(JNI 등)를 호출할 때 사용하는 스택.

    1-4. Code Cache

    • JIT 컴파일러가 변환한 네이티브 머신 코드를 저장하는 공간.
    • 자주 실행되는 메소드들이 여기 저장되어 → 다시 호출 시 즉시 네이티브 코드로 실행.

    2. 객체 레이아웃 (Object Layout)

    자바 객체도 결국 JVM 힙 메모리에 바이트 단위로 저장됩니다. HotSpot JVM 기준 구조는 대략 이렇게 생겼습니다:

    2-1. Header

    • Mark Word
      • 객체의 상태를 담는 다목적 필드 (hashCode, GC 상태, Lock 비트, Bias Lock 여부 등).
    • Klass Pointer
      • 해당 객체의 클래스 메타데이터(메소드 테이블, 필드 정의 등)를 가리키는 포인터.

    2-2. Compressed Oops/Class Pointers

    • 64비트 JVM이라도 메모리 절약을 위해 참조(객체 포인터)를 32비트로 압축해서 사용 가능.
    • 장점: 포인터 크기 줄어들면 메모리 풋프린트 ↓, 캐시 지역성 ↑.
    • -XX:+UseCompressedOops 옵션 기본 활성화.

    2-3. TLAB (Thread-Local Allocation Buffer)

    • 객체 생성은 자바에서 가장 빈번한 연산 중 하나.
    • 락을 잡고 힙에 할당하면 병목이 발생하기 때문에, 스레드별로 작은 버퍼(TLAB)를 할당.
    • 객체는 TLAB 안에서 단순히 포인터만 증가(bump-the-pointer)하면서 생성 → 락 없이 초고속 할당 가능.
    • Young Gen과 매우 잘 어울림.

    ✅ 요약

    • Heap: 객체 저장, Young/Old, Region 기반 GC 관리.
    • Metaspace: 클래스 메타데이터 (PermGen 대체).
    • Thread-Local: 각 스레드의 Stack/PC/Native Stack.
    • Code Cache: JIT 네이티브 코드 저장.
    • 객체 레이아웃: Header(MarkWord+Klass), Compressed Oops, Padding.
    • TLAB: 스레드별 초고속 객체 할당 버퍼.
    728x90
    반응형
Designed by Tistory.