JVM이 할당받은 메모리를 용도에 따라 나누어 관리하는 방식. 메서드 영역과 호출 스택, 힙이 대표적인 영역이다.
메서드 영역
메서드 영역에는 .class 파일을 읽어 분석한 클래스 정보와 클래스 데이터, 클래스 변수가 들어간다. 클래스가 로드될 때 자리를 잡으므로 인스턴스 생성과 상관없이 처음부터 메모리에 있고, 프로그램이 끝나 메모리에서 내려갈 때 함께 사라진다. 클래스 정보가 언제 어디에 올라가는지는 JVM과 바이트코드에 적어두었다.
호출 스택
호출 스택은 메서드가 일하는 데 필요한 공간을 내준다. 메서드를 부르면 그 메서드의 지역 변수와 매개변수가 스택에 쌓이고, 메서드가 끝나면 통째로 사라진다. 지역 변수를 메서드 밖에서 쓸 수 없는 까닭이 여기에 있다. 쌓아 올리는 구조라 한계도 있어서 재귀가 깊어지면 StackOverflowError가 난다. DFS를 재귀로 짜면 콜 스택이 되돌아갈 경로를 대신 기억해주고, 퀵 정렬의 공간복잡도가 O(log n)인 것도 재귀 깊이 때문이다.
힙과 참조
new로 만든 인스턴스와 그 인스턴스 변수는 힙에 들어간다. 참조 변수는 스택에 있고 실제 객체는 힙에 있다.
Person p = new Person();
// p (참조)는 스택, Person 객체는 힙여러 참조가 같은 힙 객체를 가리킬 수 있어서 얕은 복사와 깊은 복사 문제가 생긴다.
가비지 컬렉션
힙에 있는 것은 가비지 컬렉션의 대상이 된다. 더 이상 참조되지 않는 객체를 자동으로 수거하는 기능으로, 메모리 해제를 프로그래머가 직접 하는 C 계열과 자바가 갈라지는 지점이다. 해제를 신경 쓰지 않아도 되니 프로그래밍에 집중하게 된다.
Person p = new Person();
p = null; // 원래 객체는 아무도 참조하지 않는다기준은 참조 여부이고, 수거 시점은 알 수 없다. 보통 메모리가 부족해질 무렵이다. 불변 객체가 GC에 유리하다는 말도 이 맥락이다. 한 번 만든 것을 계속 재사용하면 새로 할당하고 수거하는 일이 줄어든다.
static으로 붙들리는 공간
static 변수는 프로그램의 시작부터 끝까지 메모리에 상주한다. 너무 큰 변수를 static으로 선언하면 그 공간을 끝까지 붙들고 있는 셈이다.
모든 변수를 멤버 변수나 static 변수로 선언하면 메모리가 낭비되고 코드도 읽기 어려워진다. 함수에서 잠깐 쓰고 마는 값이라면 지역 변수가 맞다. 인스턴스마다 달라지는 값은 멤버 변수로, 모든 인스턴스가 공유하는 값은 static 변수로 둔다.