
자바를 공부할 때 스택과 힙을 많이 들어보았다.
하지만 이 두 개념을 정확히 구분해서 설명하지 못하는 경우가 많다.
나 역시 한동안 “객체는 힙에 있다”, “변수는 스택에 있다” 같은 표현을 아무 생각 없이 받아들였다가 메서드 호출, 객체 전달, 값 변경이 엇갈리는 순간마다 계속 헷갈렸다.
자바 프로그램은 JVM 위에서 실행되며, JVM은 실행 중인 데이터를 여러 메모리 영역에 나누어 관리한다.
그중에서도 실제 코드를 이해하는 데 핵심이 되는 영역이 Stack과 Heap이다.
new로 생성된 객체와 배열이 저장되는 공간이 둘을 단순히 “여기엔 변수, 저기엔 객체”로 외우면 반드시 한 번은 개념이 무너진다.
중요한 건 무엇이 저장되느냐보다, 언제 생성되고 언제 사라지느냐다.
Stack은 메서드가 호출될 때마다 하나의 스택 프레임(Stack Frame)을 생성한다.
이 프레임 안에는 다음과 같은 정보가 들어간다.
메서드가 끝나면 해당 스택 프레임은 통째로 제거된다.
이 과정은 매우 빠르고, 개발자가 직접 관리하지 않는다.
중요한 특징은 다음과 같다.
Heap은 new 키워드를 통해 생성된 객체와 배열이 저장되는 공간이다.
Stack과 달리, Heap에 있는 객체는 메서드가 끝나도 바로 사라지지 않는다.
Heap의 핵심 특징은 다음과 같다.
여기서 자주 생기는 오해가 하나 있다.
“객체는 힙에 있고, 변수는 스택에 있다”
이 표현은 반은 맞고 반은 틀리다.
정확히 말하면,
int a = 10;
int b = a;
b = 20;
a와 b는 Stack에 값 자체를 가진다.b = a는 값 복사다.b를 바꿔도 a는 영향을 받지 않는다.즉, 기본형은 값 복사다.
class Person {
int age;
}
Person p1 = new Person();
p1.age = 10;
Person p2 = p1;
p2.age = 20;
이 코드에서 실제로 일어나는 일은 다음과 같다.
new Person() → Heap에 객체 생성p1 → Stack에 생성되고, Heap 객체의 주소값(참조값)을 가진다p2 = p1 → 참조값이 복사된다p1과 p2는 같은 객체를 가리킨다따라서 p2.age = 20은
“p2만 바꾸는 것”이 아니라, 힙에 있는 하나의 객체를 바꾸는 것이다.
여기서 핵심은 이것이다.
참조형의 대입은 객체 복사가 아니라, 참조값 복사다.
자바는 무조건 Call by Value다.
다만, 참조형의 경우 “값”이 곧 참조값일 뿐이다.
void change(Person p) {
p.age = 99;
}
Person p = new Person();
p.age = 10;
change(p);
p의 참조값이 복사되어 메서드로 전달된다void swap(Person p) {
p = new Person();
p.age = 77;
}
Person p = new Person();
p.age = 10;
swap(p);
p의 참조값이 복사된 것은 동일하다p = new Person()은 메서드 안의 지역 변수만 다른 객체를 가리키게 한다p는 전혀 영향을 받지 않는다이 두 예제의 차이는 객체를 수정했는지, 아니면 참조 변수를 다른 객체로 바꿨는지의 차이다.
객체는 다음 조건을 만족하면 GC 대상이 된다.
Person p = new Person();
p = null;
이 순간 객체는 즉시 삭제되는 것이 아니라, “더 이상 필요 없는 객체”로 판단 대상이 된다.
언제 실제로 제거될지는 GC의 판단이다.