JVM 메모리의 이해

2 minute read

JVM기반 어플리케이션을 서비스 할 때는 규모가 작을 경우는 잘모르고 지나가도 운좋게 아무 문제 없이 지나갈 수 있지만, 규모가 조금이라도 큰 경우 잘못된 JVM 메모리 설정에 의해 엄청난 문제를 초래할 수 있다. 그래서 JVM 메모리 구조와 각 요소 별 역할에 대해서 기록 한다.

Runtime Data Area

JVM 메모리라고 하면 Runtime Data Area를 말하는 것인데, 해당 영역은 아래와 같이 Method Area, Heap Area, Stack Area, PC 레지스터, Native Method Stack 이 5가지 세부 영역을 가진다.
JVM1

  1. Method Area
    클래스 파일을 읽어 분석한 뒤 클래스의 인스턴스 변수, 메소드 코드, 클래스 변수 등을 저장하는 메모리 영역.
    저장되는 내역
    • Type Information
    • Runtime Contant Pool
    • Field Information
    • Method Information
    • Class Variable
    • 외 몇가지..
  2. Heap Area
    JVM이 인스턴스를 생성하면 할당되는 메모리 영역.
    우리가 흔히 아는 Garbage Collection(이하: GC)가 동작하는 대상이 되는 영역.
  3. Stack Area
    스레드를 제어하기 위해 사용되는 메모리 영역.
    실행되는 Method가 순서대로 쌓이게 된다.
    Method가 실행되면 Stack에서 나오고, 종료되면 메모리 상에서 제거 된다.
    스레드 별로 생성된다.
  4. PC 레지스터
    Stack Area와 마찬가지로 스레드 별로 생성.
    JVM 명령 주소가 저장.
  5. Native Stack Area
    자바 이외의 언어로 작성된 코드가 할당되는 메모리 영역.

Heap Area

JVM 메모리를 조정 및 관리 하기 위해서는 Heap Area 영역이 어떻게 구성되고 동작하는지 알아야 한다. JVM2 위 그림을 보면 알 수 있듯이 Heap 영역은 Youn Generation, Old Genration, Permanent Generation 이렇게 크게 3가지 영역으로 나뉜다. 차례 대로 하나씩 알아 보자.

  1. Young Genration - (Eden, Survivor0, Survivor1)
    새롭게 생성된 인스턴스가 할당되는 영역.
    Eden 영역의 메모리가 가득찬 상태에서 또 인스턴스를 생성하면 Eden을 대상으로 Minor GC가 동작하게 되고, 생존자 객체가 하나의 생존자 공간으로 이동하게 된다.
    여기서 중요한 점은 2개의 생존자 공간 중 하나는 항상 비어 있다는 것이다. (여기서 중요하다고 한 이유는… 이거 몰라서 첨에는 왜 쓸데 없이 자꾸 하나는 0의 값을 가지냐고 의문을 품음..)
    Young Genration의 목적은 새로운 인스턴스를 빠르게 할당하고 지속적으로 사용되는 인스턴스를 Old Genration으로 이동시키는 것이다.
  2. Old Genration
    지속적으로 사용되고, Minor GC에 의해 제거되지 않은 인스턴스가 저장되는 메모리 영역이다. 흔히 우리가 아는 GC가 동작하는 대상이다.
  3. Permanent Genration
    해당 Genration은 Java8 버전이 되면서 사라 졌지만 8이하 버전을 사용하는 경우도 많으므로 알고 가야 할거 같아서 찾아서 정리해본다. Permanent Genration에는 아래와 같은 내용들이 저장된다.
    • Class의 Meta 정보
    • Method의 Meta 정보
    • Static Object
    • 상수화된 String Object
    • Class와 관련된 배열 객체 Meta 정보
    • JVM 내부적인 객체들과 최적화 컴파일러(JIT)의 최적화 정보
  4. Metaspace
    Java8이 되면서 기존의 Permanent Genration은 없어지고 Metaspace라는 영역이 생겼다. 이전의 있던 Permanent Generation을 왜 없앴는지 생각해보자. 위 그림에서 보면 알 수 있듯이 Permanent Genration은 GC의 대상이 아니다. 그런데 Permanent Genration에 저장되는 항목들 중에 GC의 대상이 되어야 할 몇가지 항목이 보인다. Static Object와 상수화된 String Object이다. 프로그램이 커지고 저 두개의 사용 빈도가 커지게 되면 JVM은 계속 해서 java.lang.OutOfMemoryError: PermGen space 오류를 나타 내게 될 것이다. 그래서 Java8에서는 이러한 문제를 개선하기 위해 기존의 Permanent Genration을 없애버리고 Metaspace를 사용하게 된 것이다.
    Metaspace에서 Static Object와 상수화된 String Object를 제외하고는 기존의 Permanent Generation 항목들은 그대로 저장한다.
    해당 영역의 설정은 -XX:MetaspaceSize, –XX:MaxMetaspaceSize 로 설정한다.

Heap Memory 설정

  1. -xms
    JVM이 시작 할 때 설정될 초기 Heap Size
  2. -xmx
    최대 Heap Size
  3. -xmn
    Young Generation Size
  4. -XX:PermGen
    초기 Permanent Generation Memoery Size
  5. -XX:MaxPermGen
    최대 크기 Permanent Generation Memoery Size
  6. -XX:MetaspaceSize
    초기 MetaspaceSize
  7. -XX:MaxMetaspaceSize
    최대 MetaspaceSize

정리

JVM 메모리 구조에 대해서 정리를 했다. 다음 정리에는 GC가 어떻게 동작하는지에 대해서 정리를 해보자.

참고 자료

https://limkydev.tistory.com/51
https://hongsii.github.io/2018/12/20/jvm-memory-structure/ https://www.holaxprogramming.com/2013/07/16/java-jvm-runtime-data-area/ https://betsol.com/java-memory-management-for-java-virtual-machine-jvm/ https://www.journaldev.com/2856/java-jvm-memory-model-memory-management-in-java

Comments