포스트

JVM 시리즈 - 핫스팟 가상 머신에서 객체 들여다보기

핫스팟 가상 머신에서 객체 들여다보기

객체 생성

가상 머신 수준에서는 어떤 과정을 거쳐 객체가 생성될까. 자바 가상 머신이 new 명령에 해당하는 바이트코드를 만나면, 이 명령의 매개 변수가, 클래스 파일 내의 상수 풀에 존재하는지 확인한다. 즉, 명령어가 가리키는 참조가 올바른지, 참조가 이미 로딩, 해석, 초기화된 클래스인지 확인한다.
로딩이 완료된 클래스라면 새 객체를 담을 메모리를 할당한다. 필요한 메모리 크기는 로딩된 클래스에서 알 수 있다. 자바 메모리에서 포인터 밀치기 방식을 통해서 새 객체의 크기 만큼 메모리를 할당한다.
Desktop View
하지만 자바 힙이 항상 규칙적인 것은 아니다. 가상 머신은 가용 메모리 블록을 목록으로 관리하기에 객체 인스턴스를 담을 수 있는 충분한 공간을 찾고 목록을 갱신한다. 이를 여유 목록(free list) 방식이라고 한다.
멀티스레딩 환경에서는 여유 메모리의 시작 포인터 위치를 수정하는 단순한 일도 스레드 세이프하지 않다. 이를 해결하기 위해서는 메모리 할당을 원자적인 방법으로 동기화를 하거나, 스레드마다 다른 전용 메모리를 미리 할당받을 수 있다. 이를 스레드 로컬 할당 버퍼 TLAB 라고 한다. 이를 활용할지는 다음 커맨드로 가능하다.

1
2
3
--XX:+/-UseTLAB

다음 단계로, 가상 머신은 각 객체에 필요한 설정을 해준다. 어느 클래스의 인스턴스인지, 메타 정보는 어떻게 찾는지, 해시 코드는 무엇인지, GC 세대 나이는 얼마인지 등의 정보가 객체 헤더에 저장된다. 이 객체는 현재 모든 필드가 기본값인 0 의 상태다. init 메서드를 이어서 실행함으로서 개발자의 의도대로 객체가 사용가능한 상태가 된다.

객체 생성 코드는 여기서 확인할 수 있다. https://github.com/huy/hotspot_jvm_notes/blob/master/create-new-object.md

객체의 메모리 레이아웃

핫스팟 가상 머신은 객체를 세 부분으로 나눠 힙에 저장한다. 객체 헤더, 인스턴스 데이터, 길이 맞추기용 정렬 패딩이다.
Desktop View

객체 헤더

객체 헤더에는 객체의 런타임 데이터, 클래스 워드가 있다. 런타임 데이터에는 해시 코드, GC 세대 나이, 락 상태 플래그 등이 있다. 클래스 워드에는 객체의 클래스 관련 메타데이터를 가리키는 클래스 포인터가 저장된다. JVM 은 이 포인터를 통해 특정 객체가 어느 클래스의 인스턴스인지 런타임에 알 수 있다.
자바 배열인 경우에는 배열 길이도 객체 헤더에 저장된다. 배열 길이를 알아야 배열 객체가 차지하는 메모리 크기를 제대로 계산할 수 있다.

인스턴스 데이터

인스턴스 데이터는 객체가 실제로 담고 있는 정보다. 필드 관련 내용, 부모 클래스 유무, 부모 클래스에서 정의한 모든 필드 등이다.

정렬 패딩

핫스팟 가상 머신의 자동 메모리 관리 시스템에서 객체의 시작 주소는 반드시 8 바이트 정수배여야 한다. 객체 헤더는 정확히 8 바이트 정수배를 가지지만, 인스턴스 데이터는 이 조건을 충족하지 못할 수 있다. 이 경우, 패딩으로 채운다.

객체에 접근하기

객체에 접근하는 방식은 자바 바상 머신 명세에서는 객체를 가리키는 참조를 활용하라고만 나와있고, 구체적인 방법은 규정하지 않았다. 따라서 이는 가상 머신에서 구현하기 나름이다. 핸들 방식 혹은 다이렉트 포인터를 사용한다.
핸들 방식에서는 자바 힙에 핸들 저장용 풀이 별도로 존재한다. 핸들 풀에서 객체 인스턴스 데이터를 가리키는 포인터와 객체 타입 데이터를 가리키는 포인터가 있다. 객체 인스턴스 데이터를 가리키는 포인터로 자바 힙의 인스턴스 풀에서 인스턴스 데이터를 가져올 수 있다. 객체 타입 데이터를 가리키는 포인터로는 메서드 영역에서 객체 타입 데이터를 가져올 수 있다.

1
2
3
4
5
객체 참조  ---->  핸들 테이블  ---->  [ 객체 메타데이터 ]
                                 \
                                  ---->  [ 객체 데이터 ]


다이렉트 포인터 방식에서는 자바 힙에 위치한 객체에서 인스턴스 데이터, 타입 데이터 모두를 접근할 수 있어야 한다. 스태그이 참조에는 객체의 실제 주소가 바로 저장되어 있다. 이 방법은 핸들을 경유하지 않기에 속도가 매우 빠르다.

1
2
3
4
5
객체 참조  ---->  [ 객체 메타데이터 ]
               \
                ---->  [ 객체 데이터 ]

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.