❓
물음표살인마 블로그
  • README
  • ALGORITHM
    • Sieve of Eratosthenes
    • Round Up
    • Binary Search
    • Union Find
    • Sorting Array
    • Lcm, Gcd
  • TechTalk Review
    • Template
  • Books
    • CS Note for Interview
      • Ch1. Design Pattern & Programming paradigm
        • 1.1.1 Singleton Pattern
        • 1.1.2 Factory Pattern
        • 1.1.4 Observer Pattern
        • 1.1.5 Proxty Pattern & Proxy Server
        • 1.1.8 Model-View-Controller Pattern
        • 1.2.1 Declarative and Functional Programming
        • 1.2.2 Object Oriented Programming
      • Ch2. Network
        • 2.2.1 TCP/IP Four-Layer Model
        • 2.2.1-1 TCP 3, 4 way handshake
        • 2.3 Network Devices L4, L7
        • 2.4.1 ARP, RARP
        • 2.4.2 Hop By Hop Communication
        • 2.4.3 IP Addressing Scheme
      • Ch3. Operating System
        • 3.1.1 Roles and Structure of Operating Systems
        • 3.2.1 Memory Hierarchy
        • 3.2.2 Memory Management
        • 3.3.1 Processes and Compilation Process
        • 3.3.3 Memory Structure of a Process
        • 3.3.4 Process Control Block (PCB)
        • 3.3.5 Multiprocessing
        • 3.3.6 Threads and Multithreading
        • 3.3.7 Shared Resources and Critical Sections
        • 3.3.8 Deadlock
        • 3.4 CPU Scheduling Algorithm
      • Ch4. Database
        • 4.1 Database Basic
        • 4.2 Normalization
        • 4.3 Transaction and Integrity
        • 4.4 Types of Databases
        • 4.5 Indexes
        • 4.6 Types of Joins
        • 4.7 Principles of Joins
      • Ch5. Data Structure
    • Learning the Basics of Large-Scale System Design through Virtual Interview Cases
      • 1. Scalability based on user counts(1/2)
      • 1. Scalability based on user counts(2/2)
      • 2.Back-of-the-envelope estimation
      • 3. Strategies for System Design Interviews
      • 4. Rate Limiter
      • 5. Consistent Hashing
      • 6. Key-Value System Design
      • 7. Designing a Unique ID Generator for Distributed Systems
      • 8. Designing a URL Shortener
      • 9. Designing a Web Crawler
      • 10. Notification System Design
      • 11. Designing a News Feed System
      • 12. Chat System Design
      • 13. AutoComplete
      • 14. Design YouTube
      • 15. Design Google Drive
      • Loadbalancer Algorithms
      • Cache tier
      • CDN, Content Delivery Network
      • Stateless Web tier
    • Computer System A programmer's perspective
    • Effective Java
      • Item 1. Consider Static Factory Methods Instead of Constructors
      • Item 2. Consider a Builder When Faced with Many Constructor Parameters
      • Item 3. Ensure Singleton with Private Constructor or Enum Type
      • Item 4. Enforce Noninstantiability with a Private Constructor
      • Item 5. Prefer Dependency Injection to Hardwiring Resources
      • Item 6. Avoid Creating Unnecessary Objects
      • Item 7. Eliminate Obsolete Object References
      • Item 8. Avoid Finalizers and Cleaners
      • Item 9.Prefer try-with-resources to try-finally
      • Item10. Adhering to General Rules When Overriding equals
        • Handling Transitivity Issues
        • Ensuring Consistency
      • Item11. Override hashCode When You Override equals
      • Item12. Always Override toString
        • Always Override toString
      • Item13. Override Clone Judiciously
      • Item14. Consider Implementing Comparable
      • Item15. Minimize the Accessibility of Classes and Members
      • Item16. Accessor Methods Over Public Fields
      • Item17. Minimize Mutability
      • Item18. Composition over inherentance
      • Item19. Design and Document for Inheritance, or Else Prohibit It
      • Item20. Prefer Interfaces to Abstract Classes
      • Item21. Design Interfaces with Implementations in Mind
      • Item22. Use Interfaces Only to define Types
      • Item23. Prefer Class Hierarchies to Tagged Classes
      • Item24. Favor Static Member Classes Over Non-Static
      • Item28. Use Lists Instead of Arrays
      • Item29. Prefer Generic Types
      • Item30. Favor Generic Methods
    • Head First Design Patterns
      • Ch1. Strategy Pattern
      • Ch2. Observer Pattern
        • Ver1. Ch2. Observer Pattern
      • Ch3. Decorator Pattern
        • Ch3. Decorator Pattern
      • Ch4. Factory Pattern
      • Ch5. Singleton Pattern
      • Ch6. Command Pattern
      • Ch7. Adapter and Facade Pattern
      • Ch8. Template Method Pattern
    • Digging Deep into JVM
      • Chapter 2. Java Memory Area & Memory Overflow
      • Chapter 3. Garbage Collector & Memory Allocation Strategy (1/2)
      • Chapter 3. Garbage Collector & Memory Allocation Strategy (2/2)
      • Chapter 5. Optimization Practice
      • Chapter 6. Class file structure
      • Chapter 8. Bytecode Executor Engine (1/2)
  • Interview Practices
    • Restful API Practices
      • Url Shortener API
      • Event Ticket Reservation API
      • Course Management API
      • Search posts by tags API
      • Online Code platform API
      • Simple Task Management API
      • Event Participation API
      • Review System API
      • Car management API
      • Online Library
    • Tech Review
      • if(kakao)
        • Kakao Account Cache Migration / if(kakao)2022
        • Improving the Anomaly Detection System for KakaoTalk Messaging Metrics / if(kakao) 2022
        • Standardizing API Case Handling Without Redeployment / if(kakaoAI)2024
        • JVM warm up / if(kakao)2022
    • Naver Computer Science
      • Process & Thread
      • TCP & UDP
      • Spring & Servlet
      • Filter & Interceptor & AOP
      • Equals() & ==
      • Dependency Injection
      • Object Oriented Programming
  • F-Lab
    • Week1
      • Client & Server
      • HTTP
      • TCP/UDP
      • REST API
      • Questions
        • Object Oriented Programming
        • HTTP
        • Process & Thread
        • Data Structure
    • Week2
      • OSI 7 layer
      • Web vs WAS
    • Week3
      • RDB vs NoSQL
      • RDB Index
      • Cache
      • Redis
      • Messaging Queue
    • Week4
      • Project - Ecommerce
    • Week5
      • ERD - 1
    • Week6
      • Ecommerce - 2
      • Role
      • pw hashing && Salt
      • CreatedAt, ModifiedAt
      • JWT
      • Copy of ERD - 1
    • Week7
      • Vault (HashiCorp Vault)
    • Week 8
      • Api Endpoints
    • Week10
      • Product Create Workflow
  • TOY Project
    • CodeMentor
      • Implementation of Kafka
      • Project Improvement (Architectural Enhancements)
      • Communication between servers in msa
  • JAVA
    • MESI protocol in CAS
    • CAS (Compare and Set)
    • BlockingQueue
    • Producer & Consumer
    • Synchronized && ReentrantLock
    • Memory Visibility
    • Checked vs Unchecked Exception
    • Thread
    • Batch delete instead of Cascade
    • Java Questions
      • Week 1(1/2) - Basic Java
      • Week 1(2/2) - OOP
      • Week 2(1/2) - String, Exception, Generic
      • Week2(2/2) Lambda, Stream, Annotation, Reflection
      • Week3(1/2) Collections
      • Week3(2/2) Threads
      • Week4 Java Concurrency Programming
      • Week5 JVM & GC
    • Java 101
      • JVM Structure
      • Java Compiles and Execution Method
      • Override, Overload
      • Interface vs Abstract Class
      • Primitive vs Object Type
      • Identity and equality
      • String, StringBuilder, StringBuffer
      • Checked Exceptions and Unchecked Exceptions
      • Java 8 methods
      • Try-with-reources
      • Strong Coupling and Loose Coupling
      • Serialization and Deserialization
      • Concurrency Programming in Java
      • Mutable vs Immutable
      • JDK vs JRE
  • SPRING
    • DIP. Dependency Inversion Principal
    • Ioc container, di practice
    • @Transactional
    • Proxy Pattern
    • Strategy Pattern
    • Template Method Pattern
    • using profile name as variable
    • Spring Questions
      • Spring Framework
      • Spring MVC & Web Request
      • AOP (Aspect-Oriented Programming)
      • Spring Boot
      • ORM & Data Access
      • Security
      • ETC
  • DATABASE
    • Enhancing Query Performance & Stability - User list
    • Ensuring Data Consistency, Atomicity and UX Optimization (feat.Firebase)
    • Redis: Remote Dictionary Server
    • Database Questions
      • Week1 DBMS, RDBMS basics
      • Week2 SQL
      • Week3 Index
      • Week4 Anomaly, Functional Dependency, Normalization
      • Week5 DB Transaction, Recovery
    • Normalization
      • 1st Normal Form
      • 2nd Normal Form
      • 3rd Normal Form
  • NETWORK
    • HTTP & TCP head of line blocking
    • HTTP 0.9-3.0
    • Blocking, NonBlocking and Sync, Async
    • Network Questions
      • Week1 Computer Network Basic
      • Week2(1/3) Application Layer Protocol - HTTP
      • Week2(2/3) Application Layer Protocol - HTTPS
      • Week2(3/3) Application Layer Protocol - DNS
      • Week3 Application Layer
      • Week4 Transport Layer - UDP, TCP
      • Week5 Network Layer - IP Protocol
    • Network 101
      • https://www.google.com
      • TCP vs UDP
      • Http vs Https
      • TLS Handshake 1.2
      • HTTP Method
      • CORS & SOP
      • Web Server Software
  • OS
    • Operating System Questions
      • Week1 OS & How Computer Systems Work
      • Week2(1/2) Process
      • Week2(2/2) Thread
      • Week3 CPU Scheduling
      • Week4 Process Synchronize
      • Week5 Virtual Memory
    • Operating System 101
      • Operating system
        • The role of the operating system
        • The composition of the operating system.
      • Process
        • In Linux, are all processes except the initial process child processes?
        • Zombie process, orphan process
        • (Linux) Daemon process
        • Process address space
        • Where are uninitialized variables stored?
        • Determination of the size of the Stack and Heap
        • Access speed of Stack vs Heap
        • Reason for memory space partitioning
        • Process of compiling a process
        • sudo kill -9 $CURRENT_PID
      • Thread
        • Composition of a thread's address space
      • Process vs Thread
        • Creation of processes and threads in Linux
      • Multiprocessing
        • Web Browser
        • Implementation of multiprocessing
        • Application areas of multiprocessing
      • Multithreading
        • Application areas of multithreading
      • Interrupt
        • HW / SW Interrupt
        • Method of handling interrupts
        • Occurrence of two or more interrupts simultaneously
      • Polling
      • Dual Mode
        • Reason for distinguishing between user mode and kernel mode
      • System call
        • Differentiation between system calls
        • Types of system calls
        • Execution process of a system call
      • Process Control Block (PCB)
        • PCB의 구조
        • 쓰레드는 PCB를 갖고 있을까?
        • 프로세스 메모리 구조
      • Context switching
        • Timing of context switching
        • Registers saved during context switching
        • Context switching in processes
        • Context switching in threads
        • Difference between context switching in processes and threads
        • Information of the current process during context switching
      • Interprocess Communication (IPC)
        • Cases where IPC is used
        • Process address space in IPC Shared Memory technique
        • Types of IPC
  • COMPUTER SCIENCE
    • Computer Architecture 101
      • 3 components of a computer
      • RAM vs ROM
      • CPU vs GPU
      • SIMD
      • Two's complement
      • Harvard Architecture vs. von Neumann Architecture
      • The structure of a CPU.
      • Instruction cycle (CPU operation method)
      • Instruction pipelining
      • Bus
      • Memory area
      • Memory hierarchy structure
        • Reason for using memory hierarchy structure
      • Cache memory
      • L1, L2, L3 Cache
      • Locality of reference (cache)
      • Fixed-point vs Floating-point
        • epresentation of infinity and NaN (Not a Number) in floating-point
      • RISC vs CISC
      • Hamming code
      • Compiler
      • Linking
      • Compiler vs Interpreter
      • Mutex vs Semaphore
      • 32bit CPU and 64bit CPU
      • Local vs Static Variable
      • Page
  • Programming Paradigm
    • Declarative vs Imperative
  • JPA, QueryDsl
    • why fetchResults() is deprecated
  • PYTHON
    • Icecream
  • FASTAPI
    • Template Page
  • LINUX
    • Template Page
  • DATA STRUCTURE
    • Counting Sort
    • Array vs Linked List
  • GIT, Github
    • git clone, invalid path error
  • INFRA
    • Template Page
  • AWS
    • Server Log Archive Pipeline
    • Image Processing using Lambda
  • DOCKER
    • Docker and VM
    • Python Executable Environment
    • Docker commands
  • docker-compose
    • Kafka, Multi Broker
  • KUBERNATES
    • !Encountered Errors
      • my-sql restarts
      • kafka producer: disconnected
    • Kubernetes Components
    • Helm
      • Helm commands
    • Pod network
    • Service network
      • deployment.yaml
      • services.yaml
    • Service type
      • Cluster IP
      • NodePort
    • service-name-headless?
    • kube-proxy
  • GraphQL
    • Template Page
  • WEB
    • Template Page
  • Reviews
    • Graphic Intern Review
    • Kakao Brain Pathfinder Review
    • JSCODE 자바 1기 Review
  • 😁Dev Jokes
    • Image
      • Plot twist
      • Priorities
      • SQL join guide
      • Google is generous
      • Genie dislikes cloud
      • buggy bugs
      • last day of unpaid internship
      • what if clients know how to inspect
      • its just game
      • how i wrote my achievement on resume
      • self explanatory
      • chr(sum(range(ord(min(str(not))))))
Powered by GitBook
On this page
  • 2-2. 런타임 데이터 영역
  • 프로그램 카운터
  • 자바 가상 머신 스택 (JVM Stack)
  • 네이티브 메서드 스택 (Native Method Stack)
  • 자바 힙 (Heap)
  • 메서드 영역 (jdk 7힙 -> jdk 8 메타스페이스)
  • 런타임 상수 풀 ( Runtime constant pool: 메서드 영역에 포함)
  • 다이렉트 메모리 (Direct Memory: JVM 관리 영역X)
  • 2-3. 핫스팟 가상 머신에서의 객체 들여다보기
  • 1. 객체 생성 과정
  • 힙 메모리 할당 방식
  • 멀티스레드 환경에서의 객체 생성 문제
  • 생성자 호출과 객체의 완전한 초기화
  • 2. 객체의 메모리 레이아웃
  • 객체 헤더 (Object Header)
  • 인스턴스 데이터
  • 정렬 패딩
  • 3. 객체에 접근하기
  1. Books
  2. Digging Deep into JVM

Chapter 2. Java Memory Area & Memory Overflow

2장. 자바 메모리 영역과 메모리 오버플로

PreviousDigging Deep into JVMNextChapter 3. Garbage Collector & Memory Allocation Strategy (1/2)

Last updated 2 months ago

2-2. 런타임 데이터 영역

프로그램 카운터

JVM이 현재 실행 중인 명령의 바이트코드 주소를 저장하는 작은 메모리 공간. JVM은 어떤 명령을 실행해야 하는지 추적할 수 있음

  • 역할

    • JVM의 바이트코드 인터프리터는 프로그램 카운터에 저장된 주소를 읽고 해당 바이트 코드 명령을 실행 ( .java -> javac compiler -> .class -> 인터프리터/JIT compiler )

    • 실행 종료 후, 프로그램 카운터의 값을 갱신하여 다음 명령어로 이동. (동적 실행 구조)

    • 흐름 제어(조건문, 반복문, 예외 처리 등)를 수행할 때도 활용

  • 멀티스레딩과 프로그램 카운터

    • JVM은 멀티스레딩을 지원하며, 각 스레드는 독립적인 실행 흐름을 가짐

    • 스레든느 번갈아가며 CPU를 점유하고, 각 스레드는 자신만의 프로그램 카운터를 별도로 유지.

    • 특정 스레드가 실행을 멈췄다가 다시 실행될 때, 프로그램 카운터의 값을 참조하여 중단된 위치에서 실행을 재개.

  • 네이티브 메서드 실행 시

    • 네이티브 메서드를 실행할 때는 프로그램 카운터를 유지할 필요X

    • 네이티브 메서드 실행중인 스레드의 프로그램 카운터 값은 Undefined.

왜 네이티브 메서드 실행중인 스레드의 프로그램 카운터 값은 Undefined인가?
  • PC(프로그램 카운터): 현재 실행 중인 바이트코드 명령어의 주소를 저장.

  • 스택(메서드 호출 프레임 저장): 메서드 호출 시 리턴 주소(다음 실행할 위치)를 저장.

자바 가상 머신 스택 (JVM Stack)

JVM 스택은 각 스레드가 실행하는 메서드 호출 정보를 저장하는 공간이고, 스레드마다 독립적으로 생성됨. 해당 공간은 JVM이 스레드의 실행 상태를 관리하는데 사용되고, 스택 프레임 단위로 구성됨.

  • 스택 프레임의 구성 요소

    • 지역 변수 테이블(Local Variable Table) - 메서드의 지역 변수들을 저장

    • 연산자 스택(Operand Stack) - 연산 과정에서 사용할 피연산자와 결과값을 임시 저장

    • 동적 링크(Dynamic Linking) - 메서드 실행 중 참조해야 할 메서드나 클래스 정보를 저장

    • 메서드 반환 주소(Return Address) - 메서드가 종료된 후 실행이 재개될 위치를 저장

  • 스택 프레임 동작 방식

    • 메서드가 호출될 때 새로운 스택 프레임이 생성되어 JVM 스택에 push 된다.

    • 메서드 실행이 종료되면 해당 스택 프레임은 pop 되어 제거 된다.

  • JVM 스택과 메모리 오류

    • StackOverflowError: 너무 깊은 재귀 호출 / 무한 루프

    • OutOfMemoryError: JVM이 스택 확장을 시도할 때 메모리가 부족하면 발생

왜 push, pop인가?
  • 메서드가 실행을 마치면, 해당 스택 프레임이 pop 되면서 이전 메서드의 실행 상태로 돌아간다.

  • 즉, 마지막으로 호출된 메서드가 가장 먼저 종료되므로 후입선출(LIFO) 원칙을 따르게 됨.

네이티브 메서드 스택 (Native Method Stack)

자바 코드가 아닌 네이티브 코드를 실행할 때 사용하는 스택. 스레드 별로 별도로 생성되며, JVM 명세에는 "네이티브 메서드 스택의 구조나 구현 방식에 대한 규정을 두지 않는다"

  • 역할

    • JNI를 통해 호출된 네이티브 메서드가 사용하는 실행 환경 제공

    • 네이티브 코드 실행을 위한 지역 변수 저장 및 연산 수행

    • 네이티브 코드 실행 중 JVM과의 인터페이스 역할 수행

  • 메모리 오류

    • 네이티브 메서드 스택 크기가 초과되면 StackOverflowError

    • 확장시 메모리가 부족하면 OutOfMemoryError

JVM에서의 실행 환경 vs 네이티브 코드 실행 환경
  • JVM에서 실행되는 자바 메서드는 자바 가상 머신 스택을 사용하며, 바이트코드를 실행하는 구조

  • 하지만 JNI (Java Native Interface)를 통해 C, C++ 같은 네이티브 코드를 실행하려면 자바가 아닌 시스템의 실행 환경이 필요

  • 네이티브 코드가 실행될 때 해당 스레드의 JVM 스택이 아니라 네이티브 메서드 스택을 사용하여 실행 환경을 제공

실행 환경이 필요한 이유
  • 네이티브 코드는 CPU의 기계어 명령을 실행하는 것이므로 실행 자체는 OS에서 이루어지고, 네이티브 메서드 스택은 실행에 필요한 부가적인 데이터를 저장하는 역할을 한다

  • JVM 내부에서 실행되는 코드는 바이트코드를 실행하지만, 네이티브 코드는 CPU의 기계어 명령을 직접 실행

  • JVM이 직접 관리하지 않는 OS의 메모리, CPU 레지스터, 스레드 스케줄링 등을 다룰 필요

  • 그래서 JVM이 직접 다루지 않는 네이티브 코드를 실행할 별도의 공간(네이티브 메서드 스택)이 필요

  • 연산 결과를 변수에 저장해야 하는 경우, 네이티브 스택에 저장

네이티브 코드 실행을 위한 지역 변수란?

네이티브 메서드 스택도 자바 가상 머신 스택과 마찬가지로 메서드 실행 중 필요한 지역 변수(스택 프레임 내 변수)를 저장하는 공간을 제공

지역 변수 저장이 필요한 이유

  • 스레드별 독립적인 데이터 저장: 네이티브 메서드는 다중 스레드 환경에서도 독립적으로 실행될 수 있어야 함.

  • 메서드 실행이 끝나면 자동 해제: 네이티브 메서드가 끝나면 지역 변수도 함께 제거됨.

네이티브 코드 실행 중 JVM과의 인터페이스 역할

네이티브 코드가 JVM과 상호작용하는 기능을 제공한다는 의미 (네이티브 코드에서 JVM 내부의 기능(예: 객체 생성, 메서드 호출, 필드 접근)을 사용할 수 있도록 하는 역할)

  • JNI를 이용한 자바 객체 조작

    • 네이티브 코드에서 자바 객체를 생성하고 조작할 수 있음.

  • 네이티브 코드에서 자바 메서드 호출

    • 네이티브 코드에서 자바 클래스의 메서드를 호출할 수 있음

  • 예외 발생 및 처리

    • 네이티브 코드에서 자바 예외를 던질 수 있음(예: NullPointerException)

  • GC와의 상호작용

    • JNI를 통해 자바 객체를 네이티브 코드에서 사용하면, JVM GC가 이를 알아서 수집하지 않도록 관리해야 함

자바 힙 (Heap)

JVM에서 가장 큰 메모리 영역으로, 모든 객체 인스턴스가 저장되는 공간이며, JVM 시작과 함께 생성되고 모든 스레드가 공유함.

  • 역할

    • 객체 인스턴스 및 배열 저장

    • GC에 의해 관리됨

    • 메모리 회수 및 재사용을 통해 효율적 관리

  • 힙 영역 세분화 (Generatinal Garbage Collection)

    • Young(Eden, Survivor)

    • Old

    • Permanent Generation(JDK7까지): 클래스 메타데이터 저장(JDK8 이후 메타스페이스로 변경됨)

  • 메모리 오류

    • OutOfMemoryError: 힙 메모리가 부족하여 새로운 객체를 할당 할 수 없을 때 발생

JDK 8부터는 PermGen이 제거되고, 대신 메타스페이스가 도입

메타스페이스는 JVM 힙 영역이 아니라 네이티브 메모리를 사용해.

  • 즉, OS에서 직접 메모리를 할당받는 방식.

  • 반면, 힙은 JVM이 직접 관리하는 메모리 공간.

메타스페이스의 장점

  • 더 이상 힙 크기에 제한되지 않음

  • 클래스 로딩이 많은 애플리케이션에서 OutOfMemoryError 감소

    • PermGen은 제한된 크기로 인해 클래스 로딩이 많으면 OOM이 발생했지만, 메타스페이스는 OS 메모리를 활용하므로 상대적으로 유연

JDK 버전별 GC의 변화

Stop-the-World(STW) 시간을 줄이고, 성능을 최적화하는 방향으로 발전

  • 1.3 - Parallel GC 도입 (Serial GC만 있던 시절에서 멀티스레드 지원)

  • 1.4 - CMS(Concurrent Mark-Sweep) GC 도입 (STW 시간을 줄이는 첫 GC)

  • 5 - Parallel GC 개선 (병렬 GC 성능 향상)

  • 6 - CMS GC 개선 (Concurrent 모드 실패 문제 해결)

  • 7 - G1(Garbage First) GC 도입 (Old GC보다 효율적)

  • 8 - G1 GC가 기본 GC로 변경 (PermGen 제거 → Metaspace 도입)

  • 9 - G1 GC 개선 (Full GC 발생 감소)

  • 10 - Parallel Full GC 지원 (G1 GC 성능 향상)

  • 11 - ZGC 도입 (초저지연 GC)

  • 12 - Shenandoah GC 도입 (낮은 STW 지연 목표)

  • 14 - G1 GC 성능 최적화, NUMA 지원

  • 15 - ZGC가 Production Ready (JDK 11에서는 Experimental)

  • 16 - ZGC가 Thread Stack 처리 지원, Heap 영역 확장

  • 17 - ZGC 개선, G1 GC의 STW 시간 감소

  • 18 - G1 GC와 Parallel GC의 강제 Full GC 튜닝 가능

  • 19 - Generational ZGC 도입 (ZGC를 Young/Old 구조로 분리하여 효율 증가)

  • 20 - Shenandoah GC 최적화, G1 GC 개선

  • 21 - Generational ZGC 공식 도입 (최신 GC 개선)

🔹 JDK별 주요 GC의 변화 상세 분석

🔹 JDK 1.3 이전 → 기본 GC: Serial GC

  • 단일 스레드로 실행되는 GC로, 멀티스레드 환경에서는 비효율적.

  • 작은 애플리케이션에서는 쓸 수 있지만, 대규모 애플리케이션에서는 성능 문제 발생.

🔹 JDK 1.4 → CMS(Concurrent Mark-Sweep) GC 도입

  • STW 시간을 줄이기 위해 처음으로 "동시(Concurrent) GC"가 도입됨.

  • CMS GC는 백그라운드에서 가비지 수집을 수행하여 애플리케이션의 중단 시간을 줄임.

  • 하지만, Concurrent Mode Failure(동시 모드 실패) 문제가 있었고, 프래그멘테이션(Fragmentation) 문제가 심각했음.

🔹 JDK 7 → G1(Garbage First) GC 도입

  • Old Generation GC의 단점을 보완하기 위해 등장.

  • 기존 CMS GC의 프래그멘테이션 문제를 해결하고 Region 기반의 메모리 관리 방식을 도입.

  • JDK 8에서 기본 GC로 변경됨.

🔹 JDK 8 → G1 GC 기본 적용 & Metaspace 도입

  • G1 GC가 기본 GC가 됨. (기존 Parallel GC 대신)

  • PermGen(Permanent Generation)이 제거되고, Metaspace로 변경됨.

    • Metaspace는 네이티브 메모리를 활용하여 크기 제한이 사라짐.

🔹 JDK 11 → ZGC 도입 (초저지연 GC)

  • ZGC는 10ms 미만의 초저지연(ultra-low latency) GC를 목표로 개발됨.

  • 1TB 이상의 대용량 힙에서도 성능 유지.

  • STW 시간이 10ms 이하로 유지되므로 실시간 서비스에 적합.

🔹 JDK 12 → Shenandoah GC 도입

  • Red Hat이 개발한 Shenandoah GC가 공식 포함됨.

  • GC Pause(정지 시간)가 매우 짧아지고, GC의 응답성을 높이는 것이 목표.

🔹 JDK 15 → ZGC가 Production Ready

  • JDK 11에서는 실험적(Experimental)이었지만, JDK 15부터는 정식으로 지원.

  • ZGC는 4TB 이상의 대형 힙 메모리를 다룰 수 있는 GC.

🔹 JDK 19 → Generational ZGC (세대별 ZGC) 도입

  • 기존 ZGC는 세대 구분 없이 모든 객체를 동일하게 관리했지만, Generational ZGC는 Young / Old 구조를 도입하여 GC 효율을 높임.

  • 결과적으로 Full GC를 최소화하고 성능을 더욱 향상.

🔹 JDK 21 → Generational ZGC 공식 적용

  • JDK 19에서 실험적으로 도입되었던 Generational ZGC가 정식으로 포함됨.

  • ZGC의 단점이었던 "모든 객체를 동일하게 관리"하던 방식에서 벗어나 성능 개선.

메서드 영역 (jdk 7힙 -> jdk 8 메타스페이스)

메서드 영역은 모든 스레드가 공유하는 메모리 공간. 클래스 관련 정보와 JIT 컴파일된 코드 캐시를 저장

  • 저장되는 정보

    • 로드된 클래스 정보 (클래스, 인터페이스, 필드, 메서드 정보)

    • 런타임 상수 풀

    • 정적 변수

    • JIT 컴파일된 코드

  • 변화된 JDK 8 이후

    • OS가 네이티브 메모리 할당.

메서드 영역은 OS가 직접 관리하는 메모리인가?
  • 메타스페이스(Metaspace)는 OS가 직접 메모리를 할당하지만, JVM이 내부적으로 관리하는 영역

  • 즉, JVM이 요청하면 OS가 네이티브 메모리를 할당하고, JVM이 이 메모리를 메서드 영역으로 사용

  • OS의 네이티브 메모리를 사용하지만, JVM이 메타데이터를 어떻게 저장하고 해제할지는 JVM이 결정하는 구조

👉 "OS가 관리하는 공간"이라기보다는, "OS가 할당한 네이티브 메모리를 JVM이 메서드 영역으로 활용하는 구조"라고 보는 것이 정확. JVM이 메서드 영역(Metaspace)을 과도하게 사용하면, 애플리케이션(JVM)뿐만 아니라 OS의 성능에도 영향을 줄 수 있다.

런타임 상수 풀 ( Runtime constant pool: 메서드 영역에 포함)

메서드 영역의 일부로, 클래스 로딩 시 생성되는 상수 및 심볼 정보를 저장하는 공간

  • 포함되는 정보

    • 클래스, 메서드, 필드의 심볼 정보

    • String Poll (interned 문자열)

    • 상수 값 저장

  • 메모리 오류

    • 상수 풀 공간이 부족하면 OutOfMemoryError 발생

심볼 정보란?

클래스의 구조를 식별하는 데 필요한 모든 정보(클래스명, 메서드명, 필드명, 타입 등)를 포함하는 문자열 및 참조 정보의 집합

클래스, 메서드, 필드 등의 고유한 식별자를 저장하는 데이터. JVM이 클래스를 로드할 때, 클래스 파일 내의 **상수 풀(Constant Pool)**에서 이러한 심볼 정보를 읽어 클래스 메타데이터를 구성한다.

심볼 정보의 예시

  1. 클래스 이름

    • 예: com.example.MyClass

  2. 메서드 이름 및 시그니처

    • 예: void printMessage(String message)

    • 여기서 printMessage는 메서드 이름, (Ljava/lang/String;)V는 JVM 내부 시그니처

  3. 필드 이름 및 타입

    • 예: private int age;

    • age라는 필드명이 심볼 정보로 저장됨

  4. 인터페이스 이름

    • 어떤 클래스가 구현하는 인터페이스의 이름 정보

Interned 문자열이란?

JVM이 동일한 문자열 리터럴을 중복 저장하지 않고 공유하기 위해 사용하는 메커니즘

String s1 = "hello"; // 상수 풀에 "hello" 저장 String s2 = "hello"; // 같은 "hello"를 참조 String s3 = new String("hello"); // 새로운 객체 생성 String s4 = s3.intern(); // s3의 문자열을 상수 풀에서 찾음

System.out.println(s1 == s2); // true (동일한 상수 풀 객체) System.out.println(s1 == s3); // false (다른 객체) System.out.println(s1 == s4); // true (s4가 상수 풀의 "hello" 참조)

JDK 7까지는 PermGen, JDK 8부터는 네이티브 메모리?

JDK 7까지는 PermGen(Permanent Generation)에 런타임 상수 풀이 저장되었고, JDK 8부터는 네이티브 메모리 기반의 Metaspace로 이동

  • JDK 7에서는 "hello".intern() 하면 PermGen에 저장.

  • JDK 8에서는 "hello".intern() 하면 Java Heap에 저장됨.

다이렉트 메모리 (Direct Memory: JVM 관리 영역X)

JVM의 힙 영역이 아니라 네이티브 메모리를 직접 할당하는 방식으로 사용

  • 사용 목적

    • DirectByteBuffer를 활용한 고속 I/O 처리

    • 데이터 복사를 최소화하여 성능 향상

  • 메모리 할당 과정

    • ByteBuffer.allocateDirect()를 호출 -> OS에서 직접 네이티브 메모리를 할당

    • JVM 힙이 아닌 네이티브 메모리에 저장

    • JVM 내부적으로 DirectByteBuffer 객체를 생성하여 참조

    • 사용 후 명시적으로 해제해야함

  • 장점

    • 빠른 성능

      • 힙 메모리의 객체를 사용할 경우, 네이티브 코드로 데이터를 복사하는 과정이 필요.

      • 다이렉트 메모리는 OS의 네이티브 메모리를 직접 사용하므로 즉시 접근 가능(I/O 최적화)

    • GC 영향없음

      • GC로 인한 성능 저하 없음

    • 대용량 데이터 처리 최적화

      • 네트워크 통신, 파일 입출력, 데이터베이스 연동 등 대용량 데이터를 처리할 때 효율적.

  • 제한 사항

    • -Xmx 등의 설정으로 JVM의 힙 크기를 조절할 수 있지만, 다이렉트 메모리는 JVM의 관리 대상이 아니므로 메모리 누수 방지를 위해 개발자가 명시적으로 해제해야함.

    • 운영 체제의 물리 메모리 한계를 초과할 경우 OutOfMemoryError 발생 가능

DirectByteBuffer란?

DirectByteBuffer는 JVM 힙이 아닌 OS의 네이티브 메모리를 직접 사용하는 버퍼를 관리하는 Java NIO(ByteBuffer) 구현 클래스

// DirectByteBuffer를 이용한 파일 I/O 성능 최적화
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class DirectByteBufferFileIO {
    public static void main(String[] args) throws Exception {
        // 파일 채널 생성
        FileInputStream fis = new FileInputStream("input.txt");
        FileOutputStream fos = new FileOutputStream("output.txt");
        FileChannel inputChannel = fis.getChannel();
        FileChannel outputChannel = fos.getChannel();

        // DirectByteBuffer 할당 (네이티브 메모리 사용)
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

        while (inputChannel.read(buffer) != -1) {
            buffer.flip();
            outputChannel.write(buffer);
            buffer.clear();
        }

        inputChannel.close();
        outputChannel.close();
        fis.close();
        fos.close();
    }
}

JVM 힙을 거치지 않고 다이렉트 메모리를 사용하여 파일 데이터를 빠르게 복사
네이티브 메모리를 활용하므로 I/O 성능이 향상됨

2-3. 핫스팟 가상 머신에서의 객체 들여다보기

new 키워드를 사용하여 객체를 생성하지만, 내부에서는 추가 과정들이 존재.

1. 객체 생성 과정

  1. 클래스 로드 확인

    1. 해당 객체의 클래스가 이미 메모리에 로드되었는지 확인

    2. 로드되지 않았다면 클래스 로딩 -> 해석 -> 초기화 단계를 거침

  2. 힙 메모리 할당

    1. 객체의 크기를 결정하고 적절한 힙 공간을 찾아 메모리를 할당

    2. 메모리 할당 방식에는 포인터 증가(Bump-the-pointer)와 Free List 방식이 있음

  3. 객체 헤더 설정

    1. 객체의 기본 정보를 설정 (해시값, GC 연령 정보 등)

  4. 필드 초기화

    1. 객체의 모든 필드를 기본값으로 설정

  5. 생성자 호출

    1. init 메서드를 실행하여 객체를 최종적으로 초기화

힙 메모리 할당 방식

객체용 메모리 공간 할당은 자바 힙에서 특정 크기의 메모리 블록을 잘라 주는 일. 객체에 필요한 메모리 크기는 클래스를 로딩하고 나면 완벽히 알 수 있음.

  • 포인터 증가 방식 (Bump-the-pointer)

    • 자바 힙이 연속적인 메모리 블록으로 관리될 때 사용

    • 사용중인 메모리와 여유 메모리 사이의 경계를 포인터로 추적

    • 새로운 객체를 생성하면 포인터를 해당 크기만큼 이동하여 메모리를 할당

    • GC가 한 번 실행될 때마다 메모리를 재정렬 해야함(Compact 필요)

    • 단편화되지 않을 시에 매우 빠른 할당 속도.

  • Free List 방식

    • 조각난 메모리 공간이 존재할 떄 사용(Compact 불필요)

    • 사용 가능한 빈 메모리 블록 목록을 유지하고 거기에서 적절한 크기의 공간을 찾아 객체를 할당

    • 메모리 관리가 유연하지만, 할당 속도가 상대적으로 느림

Compact를 수행하는 GC, 수행하지 않는 GC

✅ Serial GC

  • 단일 스레드 기반의 GC이며, Stop-the-World(전체 애플리케이션 일시 중지) 동안 Compact를 수행

  • Young GC(Minor GC)에서 Copying GC 방식을 사용하여 Compact 불필요

  • Old GC(Major/Full GC)에서 Mark-Sweep-Compact 방식으로 압축 수행

✅ Parallel GC (Throughput GC)

  • 여러 개의 스레드를 사용하여 GC 성능을 향상

  • Old GC에서 Mark-Sweep-Compact 방식을 사용하여 단편화 해소

  • Young GC는 Eden → Survivor로 이동하는 과정에서 Compact 불필요

✅ G1 GC (Garbage-First GC)

  • G1 GC는 Region 기반 GC로 동작하며, Mixed GC 단계에서 Compact 수행 가능

  • 사용되지 않는 Region을 정리하고, 유효한 객체를 새로운 Region으로 이동하여 단편화 해소

  • Full GC 발생 시에도 압축을 수행하지만, Full GC 자체는 가급적 피하도록 설계됨

❌ CMS GC (Concurrent Mark-Sweep)

  • Stop-the-World 시간을 줄이기 위해 압축을 수행하지 않음

  • 단점: 메모리 단편화(Fragmentation)가 발생할 수 있음

  • 단편화를 해결하기 위해 Full GC가 강제 발생할 수도 있음

  • JDK 9부터 G1 GC가 CMS GC를 대체

❌ ZGC

  • Heap을 조각난 상태로 유지한 채 메모리를 관리하여 Compact 불필요

  • Large Page 메모리 모델과 색인 기반 메모리 매핑을 사용

  • 즉, ZGC는 단편화 발생을 방지하는 방식으로 설계되어 Compact 자체가 필요 없음

❌ Shenandoah GC

  • Concurrent Compaction을 수행하여 Stop-the-World 없이 단편화를 해결

  • 기존 GC처럼 모든 객체를 이동하는 압축 방식이 아니라, 필요할 때만 일부 객체를 이동

  • 기본적으로는 Compact가 필요 없는 구조

멀티스레드 환경에서의 객체 생성 문제

여유 메모리의 시작 포인터 위치를 수정하는 작업도 thread-safe 하지 않기 떄문에 race condition 발생 가능.

해결 방법은 두 가지 방법이 있다.

  • CAS

    • 객체를 할당하는 과정에서 CAS 연산을 활용하여 포인터 갱신을 원자적으로 처리

    • 포인터 값을 비교 후 안전하게 변경

  • TLAB (Thread-local allocation buffer)

    • 각 스레드가 자신만의 작은 힙 영역을 별도로 할당하여 객체를 생성

    • 스레드 간 경쟁 없이 독립적인 공간에서 객체를 빠르게 생성 가능

    • TLAB이 가득 차면 새로우 ㄴ힙 영역을 요청하여 사용

    • JVM 옵션으로 -XX;+UserTLAB을 설정하면 활성화 가능

    • JVM은 TLAB을 기본적으로 활성하여 사용

TLab에서 스레드에게 충분한 영역을 줄 수 있는지 판단 방법

결론:

TLAB이 파편화된 힙에서 충분한 공간을 제공할 수 있는지는 다음과 같은 기준을 통해 판단된다.

  1. TLAB이 최소 크기(-XX:MinTLABSize) 이상인지 확인

  2. 남은 TLAB 공간이 설정된 비율(-XX:TLABWasteTargetPercent) 이하인지 확인

  3. 힙에 충분한 연속적인 공간이 존재하는지 확인

  4. 필요한 경우, 기존 TLAB 크기를 축소하거나 글로벌 힙을 활용

즉, TLAB이 충분한 공간을 찾지 못하면 글로벌 힙을 활용하여 객체를 할당하게 되고, JVM은 GC를 통해 단편화를 해결하며 TLAB 크기를 동적으로 조정한다.

👍 핵심 요약

  • TLAB을 할당할 수 없는 경우, 최소 크기 이하인지 확인 후 글로벌 힙에서 객체를 할당

  • GC가 실행될 때 단편화를 줄이고 TLAB 크기를 조정

  • JVM은 TLAB 크기를 동적으로 조절하여 최적화

  • TLAB 크기 조정은 -XX:TLABSize, -XX:TLABWasteTargetPercent, -XX:MinTLABSize 등의 옵션으로 튜닝 가능

자세히:

핫스팟 JVM에서는 TLAB 크기 결정 및 확장 정책을 사용

  1. TLAB 초기 크기 설정 - TLAB 크기는 기본적으로 -XX:TLABSize 옵션을 통해 설정할 수 있지만, JVM은 다음을 고려하여 자동으로 TLAB 크기를 결정

    • 현재 힙 메모리 상태

    • 스레드 수

    • Young Generation의 크기 (TLAB은 Young Generation에 할당됨)

    • 스레드별 객체 생성 빈도

스레드마다 동일한 크기의 TLAB을 할당하는 것이 아니라, JVM이 동적으로 크기를 조정

  1. TLAB 할당 시 충분한 공간이 있는지 판단하는 기준

    1. 기본적인 최소 크기 조건

      • TLAB이 할당될 최소 크기는 객체 크기보다 커야 함

      • -XX:MinTLABSize 옵션으로 최소 크기를 조정 가능

      • 만약 현재 힙에 TLAB 최소 크기보다 작은 연속적인 공간만 남아 있다면, 해당 스레드는 TLAB을 할당받지 않고 글로벌 힙에서 직접 할당하게 됨

    2. TLAB 잔여 공간이 설정된 임계치보다 작다면 재할당

      • JVM은 TLAB 내에서 남아 있는 공간이 일정 비율 이하로 줄어들면, 새로운 TLAB을 요청하게 됨

      • -XX:TLABWasteTargetPercent 옵션을 통해 이 임계치를 조정할 수 있음

      • 기본적으로 TLAB의 1% 미만의 공간만 남아 있으면 새로운 TLAB을 할당받음

    3. 힙 단편화 고려

      • JVM은 Young Generation 내의 가용 공간이 충분하지 않거나 단편화가 심한 경우, 기존의 TLAB을 축소하여 작은 크기로 재할당할 수도 있음

      • 만약 연속된 여유 공간이 없다면, 기존 TLAB을 해제하고 글로벌 힙에서 객체를 직접 할당하는 방식으로 전환

  1. TLAB이 부족할 경우의 동작 방식

    • 새로운 TLAB을 요청

      • 연속된 여유 공간이 충분하다면 새로운 TLAB을 할당

    • TLAB 크기 축소 후 재할당

      • Young Generation의 단편화가 심하면, TLAB 크기를 축소한 후 재할당 시도

    • TLAB을 포기하고 글로벌 힙 사용

      • 단편화가 너무 심해서 TLAB을 할당할 수 없는 경우, 스레드는 글로벌 힙에서 직접 객체를 할당

**TLAB 최적화 관련 JVM 옵션

생성자 호출과 객체의 완전한 초기화

객체는 new 키워드와 함께 생성되지만, 실제로 객체가 초기화되는 것은 생성자(<init> 메서드)가 실행된 이후이다.

  • new 키워드 → 힙 메모리 할당

  • invokespecial → 생성자 호출 (초기화 수행)

  • init 메서드 실행 → 완전한 객체 생성 완료

2. 객체의 메모리 레이아웃

핫스팟 가상 머신은 객체를 세 부분으로 나누어 힙에 저장한다.

  1. 객체 헤더 - 객체의 메타데이터를 포함

  2. 인스턴스 데이터 - 실제 객체 필드 값들이 저장됨

  3. 정렬 패딩 - 메모리 정렬을 위해 추가되는 공간

객체 헤더 (Object Header)

객체 헤더는 JVM이 객체를 관리하는 데 필요한 정보를 저장하는 영역

  • 마크 워드 (Mark Word)

    • 객체의 해시 코드 (hashCode)

    • GC 연령 (Age)

    • 락 플래그 (Lock Flag, 동기화 정보)

    • 스레드 경합 상태 (Thread Contention State)

    • 평활화 스레드 타임스탬프 (Bias Locking Timestamp)

객체 헤더 크기
  • 32비트 JVM에서는 8바이트

  • 64비트 JVM에서는 16바이트

  • 배열의 경우 추가적인 4바이트 필요

왜 객체 헤더에 동기화 정보가 필요한가?

객체 헤더에 동기화 정보가 들어가는 건 모니터 락(Monitor Lock) 때문.

VM은 synchronized 키워드나 wait(), notify() 같은 동기화 기능을 사용할 때 객체 단위로 락을 관리하는데, 이때 락 정보가 객체의 헤더(Mark Word)에 저장

🔹 객체 락의 종류 (3가지 락 단계)

자바의 동기화 방식은 최적화를 위해 세 가지 락 단계를 가지고 있어.

1️⃣ Bias Lock (편향 락)

  • 한 개의 스레드가 계속 객체를 사용할 때 최적화

  • Mark Word에 스레드 ID를 저장하여 불필요한 동기화 제거

  • 락 해제 과정 없이 같은 스레드가 계속 사용 가능

  • 단, 다른 스레드가 접근하면 편향 락이 해제됨 (Revoke)

  • 🔹 성능 최적화 목적

2️⃣ Lightweight Lock (경량 락)

  • 여러 스레드가 경쟁하지만 락 충돌이 심하지 않을 때 사용

  • CAS(Compare-And-Swap) 연산을 활용하여 빠른 락 획득 및 해제

  • Mark Word에 Lock Record의 포인터 저장

  • 🔹 빠른 락 전환 및 충돌 최소화

3️⃣ Heavyweight Lock (무거운 락)

  • 경합이 심해서 경량 락을 사용할 수 없을 때

  • OS 모니터(커널 수준의 락) 사용 → 성능 저하 발생

  • Mark Word에 Monitor Object의 주소 저장

  • 🔹 성능이 낮지만, 확실한 동기화 보장

객체 헤더의 마크워드에는 락 플래그가 있는데, 그 외에, 현재 락을 획득한 스레드는 누구고, 다음 깨울 스레드는 누구고, 어떤 스레드들이 기다리고 있는지에 대한 정보는?

자바 객체의 헤더(Mark Word)에는 동기화 관련 정보가 저장되지만, "어떤 스레드가 현재 락을 가지고 있는지", "어떤 스레드들이 기다리고 있는지", "다음에 락을 얻을 스레드는 누구인지" 같은 상세한 정보는 Mark Word 자체에는 저장되지 않음

이 정보들은 객체의 "Monitor Record" 또는 "ObjectMonitor" 구조체에 저장

락 플래그 vs 스레드 경합 상태

락 플래그

객체가 현재 어떤 락 상태인지를 나타내는 객체 헤더(Object Header)의 일부이다. 이 정보는 **마크 워드(Mark Word)**에 저장되며, 객체 동기화(synchronized) 여부에 따라 값이 달라진다.

스레드 경합 상태 (Thread Contention State)

스레드 경합 상태는 JVM이 현재 실행 중인 여러 스레드 간에 어떤 경합이 발생하는지 나타내는 상태 정보이다. 즉, 락이 필요할 때 스레드 간 충돌이 발생했는지 여부를 추적하는 정보라고 볼 수 있다.

스레드 경합 상태의 역할

  • 여러 스레드가 같은 객체를 동시에 접근하려고 할 때 동기화 충돌이 발생하는지 확인

  • 특정 스레드가 락을 얼마나 오래 기다렸는지 추적하여 최적화 가능

  • 필요하면 Bias Lock을 해제하고 Lightweight 또는 Heavyweight Lock으로 전환

평활화 스레드 타임스탬프 (Bias Locking Timestamp)

JVM은 Bias Lock을 유지할지, 해제할지를 결정해야 하는데, 이를 위해 객체가 언제 Bias Lock을 가졌는지를 Bias Locking Timestamp 값으로 저장한다.

Bias Locking Timestamp가 하는 일

  1. Bias Lock이 활성화될 때, 객체의 Mark Word에 해당 스레드 ID와 함께 "타임스탬프"를 저장

  2. 이후 JVM은 주기적으로 해당 객체가 여전히 같은 스레드에서만 사용되고 있는지 검사

  3. 객체가 일정 시간 동안 다른 스레드에서 사용되지 않았다면 Bias Lock을 계속 유지

  4. 다른 스레드가 해당 객체를 사용하려고 하면 Bias Lock을 해제하고 Lightweight Lock(경량 락) 또는 Heavyweight Lock(중량 락)으로 전환

  5. 이때, Bias Locking Timestamp를 참고하여 얼마나 오랫동안 편향되어 있었는지를 기준으로 결정

즉, Bias Locking Timestamp는 JVM이 Bias Lock을 유지할지, 해제할지를 판단하는 기준이 되는 값이다.

인스턴스 데이터

객체의 실제 데이터(필드 값들)가 저장되는 공간.

  • 객체의 모든 필드(primitive + reference types) 저장

  • 부모 클래스의 필드도 함께 저장

  • 저장 순서는 JVM의 필드 할당 전략에 따라 달라질 수 있음

  • JVM 옵션 -XX:FieldsAllocationStyle을 통해 필드 정렬 방식 변경 가능

핫스팟 JVM은 기본적으로 같은 크기의 필드를 묶어 정렬(padding을 최소화)하는 전략을 사용한다. -XX:CompactFields=true 옵션을 설정하면 작은 필드를 빈 공간 없이 압축하여 저장

정렬 패딩

정렬 패딩은 메모리 정렬(alignment)을 맞추기 위해 추가되는 공간이다.

  • JVM은 8바이트 단위로 메모리를 정렬하는 경우가 많다.

  • 인스턴스 데이터 크기가 8바이트 단위로 정렬되지 않는 경우, 패딩을 추가하여 정렬을 맞춘다.

  • CPU가 메모리에 빠르게 접근하도록 최적화하기 위한 기술.

3. 객체에 접근하기

자바 객체는 **참조(reference)**를 통해 접근한다. 참조 방식은 JVM 구현에 따라 다르지만, 핫스팟 JVM은 두 가지 방식을 제공한다.

  • 핸들(Handle) 방식

    • 핸들 방식에서는 힙(heap) 외부에 별도의 핸들 풀(handle pool)을 유지

    • 핸들은 객체의 실제 주소가 아닌 핸들 풀의 주소를 저장하며, 핸들 풀은 객체의 실제 데이터 위치를 가리키는 포인터를 가진다.

    • 장점

      • 객체가 이동할 경우 핸들만 업데이트하면 되므로 참조를 갱신할 필요 없음

      • GC가 객체를 이동할 때도 핸들 주소는 그대로 유지됨

    • 단점

      • 객체에 접근할 때 항상 한 단계 추가적인 간접 참조가 필요

      • 성능이 다이렉트 포인터 방식보다 약간 느릴 수 있음

  • 다이렉트 포인터 (Direct Pointer) 방식. (핫스팟 JVM의 기본값)

    • 다이렉트 포인터 방식에서는 객체 참조가 직접 힙 메모리의 객체 주소를 가리킨다.

    • 장점

      • 객체에 빠르게 접근 가능 (한 단계 간접 참조가 없음)

      • 참조를 직접 가리키므로 CPU 캐시 최적화 가능

    • 단점

      • GC가 객체를 이동하면 참조를 업데이트해야 함

      • 메모리 주소가 직접 바뀌므로 GC의 복잡도가 증가

핸들과 다이렉트 포인터의 차이

핸들 방식 구조

  • 객체 참조 → 핸들 풀 → 객체 실제 메모리

  • 객체 이동 시 핸들만 업데이트하면 됨

다이렉트 포인터 방식 구조

  • 객체 참조 → 객체 실제 메모리

  • 객체 이동 시 참조 주소를 직접 변경해야 함