본문 바로가기
프로그래밍

JVM의 작동 원리와 메모리 관리

by 이음코드 2024. 12. 4.
반응형

Java Virtual Machine(JVM)은 Java 프로그램을 실행하기 위한 가상 환경입니다.

JVM은 코드 실행뿐 아니라 메모리 관리, 가비지 컬렉션 등 다양한 기능을 제공합니다. 이번 글에서는 JVM의 작동 원리와 메모리 관리에 대해 자세히 알아보겠습니다. 

 

1. JVM이란?

JVM은 Java Virtual Machine의 약자로, Java 바이트코드를 실행하기 위한 가상머신입니다. JVM은 플랫폼에 독립적인 환경을 제공하여, Java코드가 "Write Once, Run Anywhere" 의 모토에 맞게 어디서든 실행될 수 있게 합니다. JVM은 컴파일된 바이트코드를 기계어로 변환하여 운영 체제와 하드웨어에 맞게 실행합니다.

JVM은 Java Development Kit(JDK)와 Java Runtime Environment(JRE)의 일부로 포함되어 있으며, 바이트 코드를 로드하고 해석하며 실행하는 역할을 합니다. 이러한 과정을 통해 Java 프로그램이 실제 시스템에서 실행될 수 있습니다.

 

2. JVM의 작동 과정

JVM의 작동 과정은 여러 단계로 이루어져 있으며, 이를 통해 Java 소스 코드가 기계어로 실행될 수 있습니다. 주요 단계는 아래와 같습니다.

1) 컴파일 단계 : Java 소스 코드(.java 파일)는 먼저 자바 컴파일러(javac)에 의해 바이트코드 (.class파일)로 변환됩니다. 이 바이트코드는 플랫폼 독립적인 코드입니다.

 - .java -> 자바 컴파일러(javac) -> .class

2) 클래스 로더 시스템 : JVM의 클래스 로더는 프로그램 실행 시 필요한 클래스를 메모리에 로드합니다. 클래스 로더는 런타임 시에 동적으로 클래스를 로드하며, 이를 통해 필요한 클래스만 로드하여 메모리 효율성을 높일 수 있습니다.

3) 실행 엔진 : 바이트코드를 실제 실행하는 역할을 담당합니다. 인터프리터JIT(Just-In-Time)컴파일러를 통해 바이트코드를 기계어로 변환하여 실행합니다.

  • 인터프리터는 바이트코드를 한 줄씩 해석하여 실행합니다. 초기에는 빠른 실행을 위해 인터프리터 방식으로 실행되지만 반복적인 실행 시 JIT컴파일러를 통해 네이티브 코드로 컴파일하여 실행 속도를 높입니다.
  • JIT 컴파일러는 핫스팟(Hostspot)을 찾아 실행 빈도가 높은 메서드를 네이티브 코드로 컴파일하고 캐싱하여 성능을 향상시킵니다.

4) 카비지 컬렉터(GC) : JVM은 불필요한 객체를 자동으로 메모리에서 제거하는 가비지 컬렉션(Garbage Collection) 기능을 제공합니다. 이를 통해 메모리 누수를 방지하고 효율적인 메모리 관리를 할 수 있습니다.

 

3. JVM 메모리 구조

JVM의 메모리 구조는 여러 영역으로 나뉘어져 있으며, 각 영역은 Java프로그램 실행에 필요한 데이터를 저장하고 관리합니다. 주요 메모리 영역은 다음과 같습니다.

 

1) 메서드 영역(Method Area)

메서드 영역은 클래스 메타데이터, 상수, 정적 변수, 메서드 코드 등을 저장하는 곳입니다. 모든 스레드에서 공유되는 영역으로 프로그램 실행 중 사용되는 클래스 정보를 유지합니다. 메서드 영역은 클래스 로딩 시 메모리에 할당되며, JVM 종료 시까지 유지됩니다.

 

2) 힙 영역 (Heap Area)

힙 영역은 객체와 배열이 저장되는 공간입니다. 힙은 가비지 컬렉션의 대상이 되는 영역으로 대부분의 Java 객체가 생성됩니다. 힙 영역은 모든 스레드에서 공유되며, JVM이 관리하는 주요 메모리 영역입니다. 힙은 크게 Young Generation과 Old Generation으로 나뉩니다.

  • Young Generation : 새로 생성된 객체가 저장되는 공간입니다. 여기서는 Eden과 Survivor 영역으로 나뉘며, 객체의 생존 여부에 따라 Old Generation으로 이동합니다.
  • Old Generation : Young Generation 을 통과한 오래된 객체가 저장됩니다. 메모리에서 살아남아 오래된 객체들이 위치하며 가비지 컬렉션이 상대적으로 덜 빈번하게 발생합니다.

3) 스택 영역 (Stack Area)

스택 영역은 각 스레드마다 생성되며, 메서드 호출 시 생성된 지역 변수와 메서드 호출 정보가 저장됩니다. 메서드 호출이 완료되면 해당 스택 프레임은 삭제되며, 자동으로 메모리가 해제됩니다. 스택 영역은 빠른 속도로 접근할 수 있기 때문에 메모리 관리에서 중요한 역할을 합니다.

 

4) PC 레지스터 (PC Register)

각 스레드마다 생성되는 PC 레지스터는 현재 실행 중인 JVM 명령어의 주소를 저장합니다. Java는 멀티스레드 환경을 지원하기 때문에 각 스레드는 자신만의 PC 레지스터를 가집니다.

 

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

Java는 네이티브 코드를 호출할 수 있는 기능을 제공하며, 네이티브 메서드 스택은 이러한 네이티브 코드(C/C++등) 실행을 위한 공간입니다.

 

4. 가비지 컬렉션 (Garbage Collection)

JVM의 핵심 기능 중 하나는 메모리 관리를 위한 가비지 컬렉션(GC) 입니다. Java는 프로그래머가 명시적으로 메모리를 해제할 필요 없이 가비지 컬렉터가 필요하지 않은 객체를 자동으로 수집하여 메모리를 해제합니다. 가비지 컬렉션의 주요 목적은 메모리 누수를 방지하고, 메모리 사용을 최적화하는 것입니다.

 

1) 가비지 컬렉션의 작동 원리

가비지 컬렉션은 힙 영역에서 사용되지 않는 객체를 식별하고 이를 메모리에서 제거합니다. JVM은 Reachability(도달 가능성)을 기준으로 객체가 여전히 참조되고 있는지 판단합니다.

  • Minor GC : Young Generation에서 발생하는 가비지 컬렉션입니다. 새롭게 생성된 객체를 정리하며, 비교적 짧은 시간이 소요됩니다.
  • Major GC (Full GC) : Old Generation에서 발생하며, 전체 힙 영역을 검사하고 필요 없는 객체를 정리합니다. Major GC는 Minor GC보다 더 많은 시간이 소요됩니다.

2) GC 튜닝

성능 최적화를 위해 가비지 컬렉션의 동작을 제어하는 GC튜닝이 필요할 때가 있습니다. JVM옵션을 통해 GC방식을 설정하거나 힙 영역의 크기를 조정할 수 있습니다. 예를 들어 -Xms와 -Xmx 옵션을 사용하여 힙의 최소 및 최대 크기를 설정할  수 있습니다.

 

5. JVM과 메모리 관리의 중요성

JVM의 메모리 구조와 가비지 컬렉션을 이해하는 것은 Java 애플리케이션의 성능을 최적화하는데 매우 중요합니다. 메모리 누수나 OutOfMemoryError 같은 문제가 발생했을 때 메모리 구조와 GC 동작 방식을 이해하고 있으면 문제의 원인을 파악하고 해결하는데 도움이 됩니다.

 

6. 마무리

이번 글에서는 JVM의 작동 원리와 메모리 관리에 대해 자세히 알아보았습니다. JVM은 Java코드의 실행을 책임지고 메모리 관리를 통해 프로그램을 안정적으로 동작할 수 있도록 해줍니다. 읽어주셔서 감사합니다.

반응형