Java 객체 생성과 메모리 배치: new, reference, object header
TOC
- Overview
- new를 만나면 무엇이 생기나
- 참조 != 인스턴스
- 인스턴스는 어떻게 구성되나
- Object Header에는 무엇이 들어가나
- 인스턴스는 어떻게 GC 대상이 되나
- Conclusion
Overview
앞 글에서는 JVM Memory Structure: Stack, Heap, Method Area에서 JVM 메모리 영역을 먼저 봤다.
이번 글에서는 한 단계 더 들어가서, new를 실행했을 때 실제로 어떤 일이 일어나는지 정리한다.
핵심 질문은 세 가지다.
new를 만나면 인스턴스가 어디에 생기는가?person같은 변수는 무엇을 들고 있는가?- 인스턴스 본체에는 어떤 메타 정보가 붙는가?
new를 만나면 무엇이 생기나
아래 코드가 있다고 하자.
public class ObjectAllocationDemo {
public static void main(String[] args) {
Person person = new Person("kim");
}
}
class Person {
private final String name;
Person(String name) {
this.name = name;
}
}
이 코드에서 new Person("kim")를 만나면 JVM은 단순히 “변수 하나를 만드는 것”으로 끝내지 않는다.
대략 이런 순서로 진행된다.
- 인스턴스를 담을 메모리 공간을 확보한다.
- 인스턴스 헤더를 채운다.
- 인스턴스 필드를 초기화한다.
- 생성자를 실행한다.
- 참조를 지역 변수
person에 저장한다. (Stack)
즉, new는 “인스턴스를 heap에 만들고, 그 인스턴스를 가리키는 참조를 돌려주는 동작”으로 보면 된다.
참조 != 인스턴스
이 부분이 가장 자주 헷갈린다.
Person person = new Person("kim");
여기서 person은 인스턴스 자체가 아니다. person은 참조(reference) 이다.
실제 Person 인스턴스는 heap에 있고, person은 그 인스턴스를 가리킨다.
시각적으로 보면 이렇다.
flowchart LR
subgraph STACK["Stack"]
direction TB
REF["person reference"]
end
subgraph HEAP["Heap"]
direction TB
OBJ["Person instance"]
FIELD["name: kim"]
OBJ --> FIELD
end
REF -->|points to| OBJ
이 구조를 기억해야 하는 이유는 단순하다.
- 참조 변수는 메서드 호출이 끝나면 사라질 수 있다.
- 인스턴스는 참조가 남아 있는 동안 heap에 남는다.
- 참조가 끊긴 인스턴스는 GC의 회수대상이 된다.
여기서 중요한 문장은 이거다.
person 변수는 참조고, 실제 Person 인스턴스는 heap에 있다.
인스턴스는 어떻게 구성되나
인스턴스는 필드 값만으로 구성되지 않는다. heap에 있는 Person 인스턴스는 크게 두 부분으로 보면 된다.
| 구성 요소 | 쉽게 보는 의미 |
|---|---|
Object Header |
인스턴스를 JVM이 관리하기 위해 붙는 추가 정보 |
Instance fields |
name 같은 실제 값 |
아주 단순하게는 아래처럼 보면 된다.
Person instance
├── Object Header
└── Instance fields
Object Header에는 무엇이 들어가나
Object Header 안에서 아래 정보들이 들어간다.
mark word: 락 상태, hash code 같은 간단한 관리 정보klass pointer: 이 인스턴스가Person같은 어떤 class에 속하는지 알려주는 정보
예시 코드는 아래와 같다.
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
}
flowchart LR
OBJ["Person instance"] --> HEADER["Object Header"]
OBJ --> FIELDS["Instance fields"]
HEADER --> MARK["mark word"]
HEADER --> KLASS["klass pointer"]
new Person("kim")로 인스턴스가 만들어질 때, heap에 올라가는 Person 인스턴스는 Object Header와 Instance fields를 함께 가진다.
Object Header는 인스턴스를 위한 관리 정보다.Instance fields는 실제 데이터다.
개념적으로는 아래처럼 보면 된다.
Person instance
├── Object Header
│ ├── mark word
│ └── klass pointer
└── Instance fields
└── name = "kim"
다만 이 구조는 JVM 구현 세부다. 즉, Java 언어의 문법적 보장이라기보다 JVM 구현에서 흔히 설명하는 모델이다.
인스턴스는 어떻게 GC 대상이 되나
인스턴스는 heap에 만들어진 뒤 바로 사라지지 않는다. 참조가 남아 있는 동안은 JVM이 그 인스턴스를 살아 있는 상태로 본다.
쉽게 말하면 이렇다.
new로 인스턴스가 만들어진다.- stack의 참조가 그 인스턴스를 가리킨다.
- 참조가 남아 있으면 인스턴스는 살아 있다.
- 참조가 끊기면 GC 대상이 된다.
- GC가 필요한 시점에 회수된다.
즉, 인스턴스의 생명주기는 생성 -> 참조 유지 -> GC 대상 -> 회수로 이해하면 된다.
여기서 중요한 점은, 참조가 끊겼다고 바로 사라지는 것이 아니라는 점이다. 회수는 GC가 돌 때 일어난다.
Conclusion
new는 단순한 생성 문법이 아니다.
인스턴스를 heap에 배치하고, object header와 instance field를 만들고, 참조를 stack/local variable에 연결하는 흐름이다.