어느 날 전에 Java Garbage Collection 및 Java is Pass by Value에 관한 몇 개의 게시물을 작성했습니다. 그 후에 Java Heap Space, Java Stack Memory, Java에서의 Memory Allocation에 대해 설명하는 이메일이 많이 왔습니다. Java 및 Java EE 책 및 자습서에서는 Java의 Heap 및 Stack 메모리에 대한 언급이 많지만 프로그램 측면에서 Heap 및 Stack 메모리가 무엇인지에 대한 완전한 설명은 거의 찾아보기 어렵습니다.
Java Heap Space
Java Heap 공간은 Java 런타임이 개체 및 JRE 클래스에 메모리를 할당하는 데 사용됩니다. 객체를 만들 때마다 항상 Heap 공간에 생성됩니다. Garbage Collection은 힙 메모리에서 실행되어 참조가없는 객체가 사용하는 메모리를 해제합니다. 힙 공간에 생성된 모든 객체는 전역 액세스를 갖고 있으며 응용 프로그램의 어느 곳에서나 참조될 수 있습니다.
Java Stack Memory
Java 스택 메모리는 스레드의 실행에 사용됩니다. 이는 수명이 짧은 메서드별 값과 해당 메서드에서 참조하는 힙의 다른 객체에 대한 참조를 포함합니다. 스택 메모리는 항상 LIFO(Last-In-First-Out) 순서로 참조됩니다. 메서드가 호출될 때마다 새로운 블록이 메서드의 로컬 기본 값과 메서드 내의 다른 객체에 대한 참조를 보유하기 위해 스택 메모리에 생성됩니다. 메서드가 종료되면 블록이 사용되지 않게되고 다음 메서드를 위해 사용 가능해집니다. 스택 메모리 크기는 힙 메모리에 비해 매우 적습니다.
Java 프로그램에서 Heap 및 Stack 메모리
간단한 프로그램을 사용하여 힙 및 스택 메모리 사용을 이해해 봅시다.
package com.journaldev.test;
public class Memory {
public static void main(String[] args) { // Line 1
int i=1; // Line 2
Object obj = new Object(); // Line 3
Memory mem = new Memory(); // Line 4
mem.foo(obj); // Line 5
} // Line 9
private void foo(Object param) { // Line 6
String str = param.toString(); //// Line 7
System.out.println(str);
} // Line 8
}
아래 이미지는 위 프로그램과 관련된 Stack 및 Heap 메모리를 보여주며 기본 값, 객체 및 참조 변수를 저장하는 방법을 보여줍니다. 이제 프로그램 실행 단계를 살펴보겠습니다.
- 프로그램을 실행하면 모든 런타임 클래스가 Heap 공간에 로드됩니다. main() 메서드가 1번 라인에서 발견되면 Java 런타임은 main() 메서드 스레드에서 사용할 스택 메모리를 생성합니다.
- 우리는 2번 라인에서 원시 로컬 변수를 만들고 있으므로 이것은 main() 메소드의 스택 메모리에 생성되고 저장됩니다.
- 3번 라인에서 객체를 만들고 있기 때문에 이것은 힙 메모리에 생성되고 스택 메모리에는 그에 대한 참조가 포함됩니다. 4번 라인에서 Memory 객체를 만들 때도 비슷한 과정이 발생합니다.
- 이제 5번 라인에서 foo() 메소드를 호출할 때, 스택의 맨 위에 foo() 메소드에서 사용할 블록이 생성됩니다. Java는 값에 의한 전달이기 때문에, 6번 라인에서 foo() 스택 블록에 대한 새로운 객체 참조가 생성됩니다.
- A string is created in the 7th line, it goes in the String Pool in the heap space and a reference is created in the foo() stack space for it.
- foo() 메소드는 8번 라인에서 종료되고, 이때 스택에 할당된 foo() 메모리 블록이 해제됩니다.
- 9번 라인에서 main() 메소드가 종료되고 main() 메소드에 대한 스택 메모리가 파괴됩니다. 또한, 이 라인에서 프로그램이 종료되므로 Java Runtime이 모든 메모리를 해제하고 프로그램 실행을 종료합니다.
Java 힙 공간과 스택 메모리의 차이점
위의 설명을 바탕으로 힙과 스택 메모리 간의 다음과 같은 차이점을 쉽게 결론지을 수 있습니다.
- 힙 메모리는 응용 프로그램의 모든 부분에서 사용되지만, 스택 메모리는 실행 스레드 하나에만 사용됩니다.
- 객체가 생성될 때마다 항상 힙 공간에 저장되며 스택 메모리에는 그에 대한 참조만 포함됩니다. 스택 메모리에는 로컬 기본 변수와 힙 공간의 객체에 대한 참조 변수만 포함됩니다.
- 힙에 저장된 객체는 전역적으로 접근 가능하지만, 스택 메모리는 다른 스레드에서 액세스할 수 없습니다.
- 스택의 메모리 관리는 LIFO 방식으로 이루어지며 힙 메모리에서는 더 복잡합니다. 힙 메모리는 Young-Generation, Old-Generation 등으로 나뉘어집니다. 자세한 내용은 Java Garbage Collection을 참조하십시오.
- 스택 메모리는 짧은 수명을 가지고 있으며 힙 메모리는 애플리케이션 실행 시작부터 끝까지 지속됩니다.
- 시작 크기와 힙 메모리의 최대 크기를 정의하기 위해 -Xms 및 -Xmx JVM 옵션을 사용할 수 있습니다. 스택 메모리 크기를 정의하기 위해 -Xss를 사용할 수 있습니다.
- 스택 메모리가 가득 차면 Java 런타임이
java.lang.StackOverFlowError
를 throw하지만, 힙 메모리가 가득 차면java.lang.OutOfMemoryError: Java Heap Space
오류가 throw됩니다. - 스택 메모리 크기는 힙 메모리와 비교했을 때 매우 적습니다. (LIFO) 메모리 할당의 간단함으로 인해 스택 메모리는 힙 메모리에 비해 매우 빠릅니다.
자바 애플리케이션에서 Java Heap Space vs Stack Memory에 대한 내용은 여기까지입니다. 자바 프로그램이 실행될 때 메모리 할당에 관한 의문을 해소해 드리기를 바랍니다.
Source:
https://www.digitalocean.com/community/tutorials/java-heap-space-vs-stack-memory