자바 프로그램이 실행되면, JVM(Java Virtual Machine)은 OS(운영체제)로부터 메모리를 할당받고, 그 메모리를 자바 프로그램에 맞게 여러 영역으로 나누어 사용하게 된다.
자바 메모리 구조
자바의 메모리 구조는 크게 아래의 3개로 나뉜다

- 메서드(static) 영역
- Class의 정보, static 변수(Class 변수 = 필드), 메서드와 생성자 코드등 모든 실행 코드가 존재.
- static 영역에 있는 것은 어디서든 접근 가능함
- JVM이 종료 시( 프로그램 종료) 메모리에서 해제 됨
- 스택(Stack) 영역
- 자바 실행 시, 하나의 실행 스택 생성, 각 스택 프레임은 기본 자료형, 변수, 중간 연산 결과, 메서드 호출 정보 등을 포함
** 스택 프레임 : 스택 영역에 쌓이는 네모 박스가 하나의 프레임이다. 메서드를 호출할 때마다 하나의 스택 프레임이 쌓이고, 메서드가 종료되면 해당 스택 프레임이 제거됨**
- Heap 영역에 생성된 데이터의 참조값(주소)이 할당됨
- 힙(Heap) 영역
- 객체(인스턴스)와 배열(참조형 데이터 타입)이 생성되는 영역, new 명령어를 사용하면 이 영역을 사용한다.
- 가비지 컬렉션(GC)이 이루어지는 주요 영역, 더 이상 참조되지 않는 객체는 GC에 의해 제거됨
스택과 큐 자료 구조
- 스택(Stack)

1(넣기) -> 2(넣기) ->3(넣기) -> 3(빼기) -> 2(빼기) -> 1(빼기)
스택은 가장 대표적인 자료구조 중 하나로, 데이터를 하나씩 쌓아올린 형태의 자료구조이다.
- 가장 나중에(마지막에) 들어온 데이터가 가장 먼저 나가는 후입 선출(LIFO) 구조
- 큐(Queue)

1(넣기) -> 2(넣기) -> 3(넣기) -> 1(빼기) -> 2(빼기) -> 3(빼기)
- 후입 선출과 반대로 가장 먼저 넣은 것이 가장 먼저 나오는 것을 선입 선출 구조(FIFO)
프로그램 실행과 메서드 호출에는 스택 구조가 적합!!
스택 영역
package javabasic.memory;
public class JavaMemoryMain1 {
public static void main(String[] args) {
System.out.println("main start");
method1(10);
System.out.println("main end");
}
static void method1(int m1) {
System.out.println("method1 start");
int cal = m1 * 2;
method2(cal);
System.out.println("method1 end");
}
static void method2(int m2) {
System.out.println("method2 start");
System.out.println("method2 end");
}
}
실행 과정 : 처음 자바 프로그램을 실행하면 아래와 같은 과정을 거친다.
1. main() 실행 -> main()을 위한 스택 프레임 하나 생성
2. main() 은 method1() 호출 , method1 스택 프레임 생성 -> m1 , cal 지역 변수(매개변수 포함)를 가지므로 해당 지역 변수들이 스택 프레임에 포함 됨
3. method1() 은 method2() 호출, method2() 스택 프레임 생성 -> m2 지역 변 수(매개변수 포함)를 가지므로 해당 지역 변수가 스택 프레임에 포함 됨
4. method2() 종료 : method2() 스택 프레임 제거, m2 매개변수 제거 -> method1()에서 method2() 호출한 지점으로 돌아감
5. method1() 종료 : method1() 스택 프레임이 제거, 지역 변수(매개변수 포함) m1 , cal 도 제거 -> main()에서 method1 호출 지점으로 돌아감
6. main() 종료 : 더 이상 호출할 메서드 X, 스택프레임도 완전히 비워짐, 자바는 프로그램을 종료!!
실행결과

스택과 힙 영역
// Data 클래스
package javabasic.memory;
public class Data {
private int value; // 멤버 변수 : 힙 영역
public Data(int value) { // 생성자 : method 영역
this.value = value;
}
public int getValue() { // 메서드 : method 영역
return value;
}
}
// JavaMemoryMain2 클래스
package javabasic.memory;
public class JavaMemoryMain2 {
// 1.main() 실행 - main() 스택 프레임 생성, args 지역변수(매개변수) 스택에 저장
public static void main(String[] args) {
System.out.println("main start");
// 2-1. method1() 실행
method1();
System.out.println("main end");
}
// 2-2. method1() 실행시 -> method1() 스택 프레임 생성
static void method1() {
System.out.println("method1 start");
// 지역변수 data1 (Data 인스턴스의 참조값) : 스택 영역,
// new Data(10) - Data 인스턴스(객체) 생성(실제 값) : 힙 영역
Data data1 = new Data(10);
// 3-1. method2() 실행 -> data1의 참조값이 data2의 매개변수로 넘어감
// data1과 data2는 동일한 힙 영역의 Data 인스턴스(객체)를 가리킴
method2(data1);
System.out.println("method1 end");
}
// 3-2. method2 실행 -> method2() 스택 프레임 생성
// 지역 변수(매개변수) data2 스택에 저장, data1과 같은 Data 인스턴스(객체)를 참조
static void method2(Data data2) {
System.out.println("method2 start");
// method1에서 넘어온 data1과 동일한 객체 참조
System.out.println("data.value : " + data2.getValue());
System.out.println("method2 end");
}
}
실행 과정은 아래의 그림 처럼 진행된다. (위 코드의 주석을 참고)

종료과정은
(3) method2() 종료 -> method2() 스택 프레임 제거, data2 제거 (파란색 참조값)
(2) method1() 종료 -> method1() 스택 프레임 제거, data1 제거 (녹색 참조값)
--> 이제 x001 참조 값을 가진 Data 인스턴스(객체)를 참조하는 곳이 없다. 즉 사용되는 곳이 없다는 뜻!
--> 결과적으로 프로그램에서 더는 사용하지 않는 객체(메모리만 차지)
--> GC(가비지 컬렉션)은 이렇게 참조가 모두 사라진 인스턴스를 찾아서 메모리에서 제거한다
실행 결과

이렇게 자바 메모리 구조에 대해 작성해 보았다!!