JAVA :: 자바 메모리 구조 - 2. 힙 영역(Heap Area)과 스택 영역(Stack Area) 비교
자바 메모리 구조
이전 포스트에서 자바의 메모리 구조는 크게 메소드 영역, 힙 영역, 스택 영역으로 나뉘어 있다는 것과, 메소드 영역이 뭔지 살짝 알아봤다.
이번에는 힙 영역과 스택 영역은 뭔지, 각각 어떤 역할을 하는지,
좀 헷갈리니까 친절히 코드로 비교도 해볼까 한다.
2. 스택 영역(Stack Area)
스택 영역에는 지역변수와 매개변수가 저장된다.
그렇담 지역변수는 뭐고, 매개변수는 뭔가여.
한글로 접하니까 오히려 헷갈리는데 지역변수 = 로컬변수(local variable), 매개변수 = 파라미터(parameter)라고 들으면 아~~ 뭐야. 할 것임.
이래도 모르겠으면 로컬변수는 메소드 내에서 선언된 변수들, 매개변수는 메소드에 아규먼트로 넘겨주는 값들로 이해하고 넘어가자.
로컬변수와 매개변수의 특징은 이 아이들이 선언된 그 블록 안에서만 유효한 변수들이라는 점이다.
이런 친구들이 스택 영역에 저장되는 것임.
쉽게 말해 스택 영역에는 프로그램의 실행 과정에서 '임시로 할당'되고, 그게 끝나면 바로 소멸되는 것들이 저장된다.
즉 => 메소드가 호출될 때마다 그 메소드의 로컬 변수를 준비하고, 메소드 호출이 끝나면 그 메소드를 위해 준비했던 모든 변수가 스택에서 제거된다.
<참고> 밑에서 설명하겠지만, 빠른 이해를 위해 덧붙이자면 참조변수에 저장되는 메모리주소는 스택영역에 저장되지만, 그 주소가 가리키는 메모리는 모두 힙 영역에 저장된다. |
코드로 보자.
<스택 영역>
public class StackMemoryTest {
public static void m1(int a) {
m2(++a);
System.out.printf("m1():%d\n", a);
}
public static void m2(int a) {
m3(++a);
System.out.printf("m2():%d\n", a);
}
public static void m3(int a) {
++a;
System.out.printf("m3():%d\n", a);
}
public static void main(String[] args) {
int a = 20;
m1(a);
System.out.printf("main():%d\n", a);
}
}
결과:
m3(): 23 m2(): 22 m1(): 21 main(): 20 |
여기서 이런 결과가 나오는 이유는, 먼저 들어간 것이 나중에 나오는 스택 구조의 특성 때문이다.
위 코드의 흐름을 보기 좋게 표현하면 대략 이렇다.
실행 코드 | 스택 영역 메모리 | 출력 | ||||
main() { int a = 20; m1(a); ... } |
|
|||||
m1(a) { m2(++a); ... } /* a 자리에 a + 1이 놓임 */ |
|
|||||
m2 (a) { m3(++a); ... } |
|
|||||
m3 (a) { ++a; ... } |
|
|||||
m3 (a) { ... System.out.printf("m3():%d\n", a); } |
|
m3(): 23 | ||||
m2(a) { ... System.out.printf("m2():%d\n", a); } |
|
m3(): 23 m2(): 22 |
||||
m1(a) { ... System.out.printf("m1():%d\n", a); } |
|
m3(): 23 m2(): 22 m1(): 21 |
||||
main() { ... System.out.printf("main():%d\n", a); } |
|
m3(): 23 m2(): 22 m1(): 21 main(): 20 |
이렇게 지역변수의 값은 해당 메소드가 실행되면, 그 메소드의 스택번지에 값이 저장된다.
그리고 해당 메소드가 모두 실행되고나면, 스택에서 해당 메소드의 스택번지가 제거되는 구조이다.
아래의 힙 영역과 어떻게 다른지도 밑에서 설명하도록 하겠다.
3. 힙 영역(Heap Area)
힙 영역에는 흔히 코드에서 'new'명령을 통해 생성된 인스턴스 변수가 놓인다.
어떤 메소드인지는 상관이 없다.걍 new 명령으로 만드는 메모리는 모조리 힙 영역에 보관된다고 보면 된다.
스택영역에 저장되는 로컬변수, 매게변수와 달리,
힙 영역에 보관되는 메모리는 메소드 호출이 끝나도 사라지지 않고 유지된다.
언제까지??
주소를 잃어버려 가비지가 되어 가비지 컬렉터에 의해서 지워질 때까지. 아니면 JVM이 종료될때까지.
역시 코드로 살펴보겠다.
<힙 영역>
public class HeapMemoryTest {
public static int[] m1(int a) {
int[] arr = m2(a + 1);
arr[2] = a;
return arr;
}
public static int[] m2(int a) {
int[] arr = m3(a + 1);
arr[1] = a;
return arr;
}
public static int[] m3(int a) {
int[] arr = new int[3];
arr[0] = a;
return arr;
}
public static void main(String[] args) {
int[] arr = m1(100);
for (int i = 0; i < arr.length; i++) {
System.out.printf("%d=%d\n", i, arr[i]);
}
}
}
0=102 1=101 2=100 |
실행 코드 | 스택 영역 메모리 | 힙 영역 메모리 | |||||||
main() { ... } |
|
||||||||
main() { int[] arr = m1(100); ... } |
|
||||||||
m1(a) { int[] arr = m2(a + 1); ... } |
|
||||||||
m2(a) { int[] arr = m3(a + 1); ... } |
|
||||||||
m3(a) { int[] arr = new int[3]; arr[0] = a; return arr; } |
|
<7000번지> |
|||||||
m3(a) { int[] arr = new int[3]; arr[0] = a; return arr; } |
|
<7000번지>
|
|||||||
m3(a) { int[] arr = new int[3]; arr[0] = a; return arr; } ↓ m2(a) { int[] arr = m3(a + 1); arr[1] = a; return arr; } |
|
<7000번지>
|
|||||||
m2(a) { int[] arr = m3(a + 1); arr[1] = a; return arr; } |
|
<7000번지>
|
|||||||
m2(a) { int[] arr = m3(a + 1); arr[1] = a; return arr; } ↓ m1(a) { int[] arr = m2(a + 1); arr[2] = a; return arr; } |
|
<7000번지>
|
|||||||
m1(a) { int[] arr = m2(a + 1); arr[2] = a; return arr; } |
|
<7000번지>
|
|||||||
m1(a) { int[] arr = m2(a + 1); arr[2] = a; return arr; } ↓ main() { int[] arr = m1(100); ... } |
|
<7000번지>
|
|||||||
main() { ... for (int i = 0; i < arr.length; i++) { System.out.printf("%d=%d\n", i, arr[i]); } } |
|
<7000번지>
|
결과 출력:
0=102
1=101
2=100
이미 눈치가 빠른 사람은 알아차렸겠지만
이렇게 인스턴스를 별도의 힙 영역에 할당하는 이유는
인스턴스의 소멸방법과 소멸시점이 지역변수(스택영역에 할당되는 애들)와는 다르기 때문이다.
(이전 포스트에서 굳이 메모리 영역을 몇 개로 나눠서 관리하는 이유가, 일상생활에서도 용도에 따라 물건을 다른 장소에 관리하는게 편한것과 같은 맥락이라고 설명했다.)
<참고2> 이미 알고 있으리라 믿지만. 8가지 원시타입(byte, short, int, long, float, double, char, boolean)을 제외한 그외의 타입으로 정의된 변수들은 모조리 레퍼런스 변수, 즉 참조변수이다. 이런 참조변수들은 실행될 때마다 많은 데이터들을 스택메모리 영역에 뒀다 뺐다 하는게 매우 비효율적이므로, 힙 영역에 그 내용(진짜 값)이 저장되고, 스택 메모리에는 간단하게 그 주소만 저장이 되는 것이다. 그리고 힙 영역에는 실제 그 변수가 가리키고 있는 값들이 저장되어 있다. |
정리
스택 영역의 지역변수들은 메소드가 호출되고 끝이 나는 프로그램의 실행 흐름에 관여하는 것들이고
힙 영역의 인스턴스들은지역변수가 참조하고 있는 실제의 값들을 가지고 있다.
---