스레드와 메모리
앞서 자바의 메모리 구조 포스팅에서 Stack과 Heap, Method Area에 대해 알아봤다.
스레드의 경우 JVM 메모리를 이해하는 것이 중요하다. 그러므로 간단하게 Stack과 Heap, Method Area에 대해 정리해 보고 시작하자.
Stack
Stack은 Stack Frame 단위로 나누어져 있으며, 새로운 메서드가 실행되거나 탈출할 때마다 Stack Frame이 생성 및 소멸을 한다. Stack Frame에는 메서드 내부의 로컬 변수를 관리하는 `Local Variables Array`와 연산 과정을 일시적으로 저장하는 `Operand Stack`, 그리고 메서드 종료 시 돌아와야 할 주소나 그 외 메서드에 대한 정보를 가지고 있는 Frame Data로 구성되어 있다.
Heap
Heap은 인스턴스 생성 시 인스턴스가 저장되는 메모리 공간이다. 인스턴스는 참조 여부에 따라 `Garbage Collector`에 의해 수집 및 소멸되며, 참조 값을 이용하여 인스턴스의 메모리 공간에 접근할 수 있다. 그 외에도 문자열 상수를 관리하는 `String Constant Pool`(문자열 상수 풀)이 Heap 메모리 공간에 포함되어 있다.
Method Area
Method Area는 클래스 정보, 필드 정보, 메서드에 대한 정보, 생성자 정보, 인터페이스 등 자바 프로그램을 실행시키기 위한 다양한 정보를 가지고 있다. 클래스 정적 변수와 정적 메서드, 상수 관련한 데이터를 가지고 있는 `Run-time Constant Pool`도 포함하고 있다.
스레드와의 관계?
스택 영역을 더 정확히 표현하자면, 각 스레드별로 하나의 실행 스택이 생성됨을 나타낸다. 따라서 스레드 수만큼 스택이 생성된다.
스레드와 자바 메모리 연관성에서 가장 중요하게 알아두어야 할 부분이 있는데 자원 공유에 대한 내용이다.
스레드는 Stack을 제외한 Heap과 Method Area 메모리 공간을 공유한다.
이 말은 즉, 스레드는 자기 자신의 Stack Memory를 가지고 있으며, 각 스레드끼리의 영역을 침범할 수 없다.
스레드의 경우 인스턴스 변수나 정적 변수는 공유할 수 있으며, 메서드 내의 로컬 변수(지역 변수)는 스레드에 스택영역에서 운용되므로 다른 스레드와 공유할 수 없다.
그렇기 때문에 JVM 힙영역에 대한 설명에서 멀티스레드 환경에서 공유되는 객체는 동기화가 필요하다는 말이 나오게 된다. 메서드 영역에서도 static 변수, 메서드를 여러 스레드에서 사용할 수 있으므로 마찬가지로 동기화 문제가 발생할 수 있다.
요약정리
메모리 | 영역공유 | 역할 | 스레드와의 관계 |
메서드 영역 | ✅ 공유 | 클래스 정보, static 변수 저장 | 여러 스레드가 공유 |
힙(Heap) | ✅ 공유 | 객체, 인스턴스 변수 저장 | 동기화 문제 발생 가능 |
스택(Stack) | ❌ 개별 | 메서드 실행을 위한 프레임 저장 | 스레드별 독립적 (안전) |
예제코드로 알아보자.
다음은 JVM 메모리 구조를 반영한 스레드 코드이다.
class SharedResource {
// 🔹 메서드 영역(Method Area) - 모든 스레드가 공유하는 static 변수
static int staticCounter = 0;
// 🔹 힙(Heap) - 인스턴스 변수 (객체마다 따로 존재)
int instanceCounter = 0;
// 메서드 실행 시, 각 스레드의 스택에서 호출됨
public void increment() {
// 🔹 스택(Stack) - 지역 변수 (스레드별 개별)
int localCounter = 0;
staticCounter++; // 모든 스레드가 공유
instanceCounter++; // 동일 객체를 사용하는 스레드는 공유, 아니면 독립적
localCounter++; // 각 스레드에서 개별적으로 증가
System.out.println(Thread.currentThread().getName() +
" - staticCounter: " + staticCounter +
", instanceCounter: " + instanceCounter +
", localCounter: " + localCounter);
}
}
class WorkerThread extends Thread {
private SharedResource resource;
public WorkerThread(SharedResource resource) {
this.resource = resource; // 힙 영역의 객체를 참조
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
resource.increment(); // 힙 영역 객체의 메서드 호출 (스택에서 실행)
}
}
}
public class JVMThreadExample {
public static void main(String[] args) {
// 🔹 힙(Heap) - 객체를 생성 (모든 스레드가 공유할 수 있음)
SharedResource sharedResource = new SharedResource();
// 스레드 생성 및 실행
Thread t1 = new WorkerThread(sharedResource);
Thread t2 = new WorkerThread(sharedResource);
t1.start();
t2.start();
}
}
해당 코드는 메서드 영역의 `staticCounter`, 힙 영역의 `instanceCounter`, 스택 영역의 `localCounte`r 변수를 포함하며, 하나의 객체를 생성한 후 두 개의 스레드를 실행하여 각각의 변수 변화 과정을 확인하는 코드이다.
코드에서 JVM 메모리와 스레드의 관계이다.
JVM 메모리 영역 | 해당 코드에서 예제 | 특징 및 역할 |
메서드 영역 (Method Area) | `static int staticCounter;` | 모든 스레드가 공유 |
힙 영역 (Heap) | `SharedResource sharedResource = new SharedResource();` | 객체와 인스턴스 변수 저장, 공유 가능 |
스택 영역 (Stack) | `int localCounter = 0;` (메서드 내 지역 변수) | 스레드마다 독립적 |
실행 결과(순서는 다를 수 있다.)
Thread-0 - staticCounter: 1, instanceCounter: 1, localCounter: 1
Thread-0 - staticCounter: 2, instanceCounter: 2, localCounter: 1
Thread-0 - staticCounter: 3, instanceCounter: 3, localCounter: 1
Thread-1 - staticCounter: 4, instanceCounter: 4, localCounter: 1
Thread-1 - staticCounter: 5, instanceCounter: 5, localCounter: 1
Thread-1 - staticCounter: 6, instanceCounter: 6, localCounter: 1
- staticCounter (메서드 영역) → 모든 스레드가 공유
- instanceCounter (힙 영역) → 객체를 공유하면 증가, 개별 객체면 독립적
- localCounter (스택 영역) → 각 스레드마다 개별적으로 유지 (항상 1)
물론 동기화에 대한 부분과 스레드 특성에 따라 결과 순서의 값은 달라질 수 있다.
참고
https://www.codelatte.io/courses/java_programming_basic/KUYNAB4TEI5KNSJV
https://www.codelatte.io/courses/java_programming_basic/KUYNAB4TEI5KNSJV
www.codelatte.io
김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성 강의 | 김영한 - 인프런
김영한 | 멀티스레드와 동시성을 기초부터 실무 레벨까지 깊이있게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을 안다?
www.inflearn.com
'Language > Java' 카테고리의 다른 글
[Java] Java8 메모리 구조 변화 (PermGen -> Metaspace) (1) | 2025.02.19 |
---|---|
[Java] 콜바이 밸류(Call by Value)와 콜바이 레퍼런스(Call by Reference) (0) | 2025.02.17 |
[Java] 제네릭(Generic) (1) | 2025.01.31 |
[Java] 자바 예외 처리 - 예외 계층, 체크 예외, 언체크 예외, 예외의 규칙 (0) | 2025.01.31 |
[Java] Class 클래스 (0) | 2025.01.30 |