본문 바로가기

JVM

Method Area 정의와 구성

 

 

Method Area란?

Method Area는 모든 스레드들이 공유하는 메모리 영역으로 JVM이 기동될 때 JVM 당 하나씩 생성된다. Garbage Collection(이하 GC)의 대상이 되는 영역으로, 클래스로더가 로드한 클래스 파일로부터 추출된 다음 9가지 정보들이 저장되어 있다. 다음 9가지 정보들은 말하자면 클래스(혹은 인터페이스)의 메타데이터들이다. 그리고 Method Area는 HotspotJVM에서 한 때는 "Permenant Generation"으로 불렸던 영역이다. 

  • Type Information
  • Run-Time Constant Pool
  • Field Information
  • Method Information
  • Class Variables
  • Reference to ClassLoader ?
  • Reference to Class ?

 

 

Permenant Generation vs. Metaspace

Permenat Generaion과 Metaspace의 차이 출처 : "Java Memory: PermGen vs MetaSpace" (이미지 클릭 시 이동)

 

  Permenant Generaion MetaSpace
GC 대상 여부 O O
Heap 여부 O X
크기 가변 여부 X O


Java8부터 HotspotJVM의 Permanent Generation이 Metaspace로 바뀐 것은 잘 알려져있다. Permenant Generation(이하 PG)은 Heap 메모리에 속했고 MetaSpace는 그렇지 않지만, 둘 다 GC의 대상이(었)다. PG는 사이즈가 동적으로 가변적이지 않아서 부족해지면 `OutOfMemoryError: PermGen`이라는 에러가 발생했던 반면, MetaSpace는 네이티브 메모리로서 그 크기가 -XX:MetaspaceSize 내에서 동적으로 가변적으로 변할 수 있어 더 이상 `OutOfMemoryError: PermGen`과 같은 에러는 발생하지 않게 되었다. 참고로 -XX:MetaspaceSize 옵션의 디폴트 값은 unlimited이다.

 

 

Metaspace의 drawback 

이 때문에 생기는 drawback도 당연히 있다. 메모리 누수가 있으면 jvm을 넘어 호스트 시스템이 메모리 부족을 겪을 수도 있다는 것이다. Metaspace는 클래스 메타데이터를 저장하는 공간으로 이와 관련한 메모리 누수로는 클래스 로더 메모리 누수(Classloader Memory Leak)가 있다. 다행인 점은 이러한 누수 문제가 발생하는 경우는 매우 한정적이다. 이에 대해서 별도의 포스트에서 설명하고자 한다. 

 

 

Type Information

Type Information이란 클래스와 인터페이스에 대한 다음과 같은 정보들을 말한다.

  • package.class 형식의 풀네임
  • 접근제어자
  • 부모 클래스의 풀네임
  • 클래스인지 인터페이스인지
  • 인터페이스인경우 인터페이스의 구현체 리스트

 

 

Run-Time Constant Pool

클래스 파일에는 상수 풀(Constant Pool)이라는 섹션이 있는데, 상수 풀은 한 클래스의 symbol 테이블이라고 할 수 있다. 상수 풀에는 해당 클래스가 참조하는 클래스들의 이름과, 변수들의 초기 값 등 데이터들이 저장되어 있다. 상수 풀은 javap이라는 클래스파일 diassembler를 사용하면 확인할 수 있는데, 아래 클래스 `Hello` 에 대한 상수풀은 다음과 같다.

class Hello {
    public static void main( String[] args ) {
        for( int i = 0; i < 10; i++ )
            System.out.println( "Hello from Hello.main!" );
	  }
}
  #1 = Methodref          #6.#16         // java/lang/Object."<init>":()V
  #2 = Fieldref           #17.#18        // java/lang/System.out:Ljava/io/PrintStream;
  #3 = String             #19            // Hello from Hello.main!
  #4 = Methodref          #20.#21        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #5 = Class              #22            // Hello
  #6 = Class              #23            // java/lang/Object
  #7 = Utf8               <init>
  #8 = Utf8               ()V
  #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               StackMapTable
  #14 = Utf8               SourceFile
  #15 = Utf8               Hello.java
  #16 = NameAndType        #7:#8          // "<init>":()V
  #17 = Class              #24            // java/lang/System
  #18 = NameAndType        #25:#26        // out:Ljava/io/PrintStream;
  #19 = Utf8               Hello from Hello.main!
  #20 = Class              #27            // java/io/PrintStream
  #21 = NameAndType        #28:#29        // println:(Ljava/lang/String;)V
  #22 = Utf8               Hello
  #23 = Utf8               java/lang/Object
  #24 = Utf8               java/lang/System
  #25 = Utf8               out
  #26 = Utf8               Ljava/io/PrintStream;
  #27 = Utf8               java/io/PrintStream
  #28 = Utf8               println
  #29 = Utf8               (Ljava/lang/String;)V

 

이러한 상수 풀의 런타임 Representaion을 런타임 상수 풀(Runtime Constant Pool)이라고 하고, 메모리 상으로는 Method Area라는 영역에 위치한다. (보너스 지식: 클래스나 인터페이스를 생성할 때, 이 런타임 상수 풀을 메모리에 다 담을 수 없게 되면 OutOfMemory 에러가 발생하게 된다.)

 

 

Field Information

클래스나 인터페이스에서 선언된 모든 필드 정보를 말한다. 필드가 선언된 순서대로 필드 정보가 기록되며, 필드 정보는 필드의 이름, 필드의 자료형, 필드의 Modifier(public, private, protected, static, final 등)를 말한다.

 

 

Method Information

클래스나 인터페이스에서 선언된 모든 메소드 정보를 말한다. 메소드의 이름, 반환 값의 자료형, 메소드 파라미터의 수와 자료형, 선언된 순서, 메소드 Modifier이다. 만약 메소드가 native나 추상 메소드가 아니라면 여기에 메소드의 바이트코드, 메소드 Stack Frame의 Operand Stakc과 Local Variable Section의 크기, Exception Table을 포함한다.

 

 

Class Variables

Class Variables는 클래스에서 static으로 선언된 변수들을 말한다. static으로 선언되면 모든 인스턴스에 공유되기 때문에 동기화 이슈가 발생하고, Class의 인스턴스 없이도 접근이 가능하다는 특징이 있다. Class Variables들은 클래스를 사용하기 전부터 미리 Method Area에 메모리를 할당 받는다. 단, Class Variable을 final로 선언하면 상수로 취급하여 Constant Pool에 Literal Constant로 저장된다.

 

 

클래스와 인스턴스의 생성과 저장

클래스에 대한 인스턴스를 생성할 때, Method Area에 저장된 클래스의 필드 정보나 메소드 정보 등을 활용하여 Heap 메모리에 객체를 하나 저장한다. 이것이 인스턴스이다. 클래스의 non-static 멤버 변수는 힙에 생성된 인스턴스에 저장이 되지만, static 변수들은 Method Area의 Class Variables에 저장된다.

 

 

Reference to ClassLoader

클래스나 인터페이스가 JVM에 로드될 때, 동일한 클래스로더가 서로 참조 관계에 있는 클래스나 인터페이스들을 모두 로드한다. Reference to ClassLoader란 해당 클래스나 인터페이스를 로드한 클래스로더 reference 정보를 말한다. 이 정보는 Dynamic Linking을 할 때 자신이 참조하는 클래스나 인터페이스를 로딩하기 위해서 (동일한 클래스 로더를 사용하기 때문에) 사용된다.

 

 

Reference to java.lang.class

클래스나 인터페이스가 JVM에 로드되면 항상 java.lang.class의 인스턴스가 하나 생성된다. Reference to java.lang.class란 그 인스턴스의 레퍼런스를 말한다. Method Area에 Reference to java.lang.class가 저장되어 있기 때문에 getName()을 통해 클래스의 이름을 얻을 수 있고, isInterface()를 통해 인터페이스 여부도 알 수 있다.

 

 

Method Table

Method Table은 메소드의 이름과 시그니처를 키로하고, 바이트코드를 가리키는 포인터를 밸류로 하는 데이터 구조이다. JVM은 Method Area에서 constant pool이 가지고 있는 reference를 통해 원하는 객체를 빈번하게 검색해야 하는데, 속도를 향상시키기 위해서 Method Table을 사용한다. Method Table은 부모 클래스의 이름과 그 포인터도 모두 가지고 있기 때문에, 상위 클래스를 거슬러 올라가는 resolution이 한 번의 lookup으로 가능해진다.

 

 

 


 

 

참고 자료