라고 JVM 메모리 구성도 모르는 감자가 말했다.
📌 OS 레벨에서의 프로세스와 스레드
프로세스
보조기억 장치에 저장된 프로그램이 메모리에 적재되어 실행되는 것으로, OS에서 자원을 할당받아 독립적으로 실행되는 단위이다. 각 프로세스는 독립적인 메모리 공간을 가지며, 다른 프로세스와 메모리 영역을 공유하지 않는다.
스레드
프로세스 내에서 작업을 수행하는 단위로, 프로세스의 메모리 공간을 공유하면서 실행된다. 하나의 프로세스에는 여러 스레드가 존재할 수 있다.
프로세스 메모리 영역
코드 영역 (Code Segment / Text Segment)
코드 영역은 프로그램의 실행 명령어가 저장되는 메모리 영역으로, 주로 실행할 바이너리 코드와 함수의 기계어 명령이 저장된다. 프로그램의 무결성과 안정성 보장을 위해 읽기 전용으로 설정되어 있어, 프로그램이 실행되는 동안 수정되지 않는다.
데이터 영역 (Data Segment)
데이터 영역은 프로그램이 실행되는 동안 전역 변수와 static 변수가 저장되는 메모리 영역이다. 데이터 영역은 초기화된 데이터 영역과 초기화되지 않은 데이터 영역으로 나뉜다.
힙 영역 (Heap Segment)
힙은 동적으로 할당되는 메모리 영역으로, 프로그램 실행 중에 필요에 따라 메모리를 할당할 수 있다. 이 영역은 가변 크기의 데이터를 저장하는 데 사용되며, 메모리 누수가 발생하지 않도록 동적 메모리 관리를 철저히 해야 한다.
스택 영역 (Stack Segment)
스택은 함수 호출 시 생성되는 로컬 변수, 매개변수, 리턴 주소 등이 저장되는 메모리 영역이다. 스택은 함수의 호출과 반환에 따라 데이터가 자동으로 추가되고 제거되는 특징이 있다. 각 스레드는 공유한 스택을 가지므로 스레드 간에 스택에 저장된 데이터가 공유되지 않는다.
📌 JVM Runt ime Data Area
위 내용이 기존에 알고 있던 지식이었는데, 그것만으로는 스레드풀을 이용한 코드를 작성할 때 어떤 부분을 신경 써야 하는지 감이 잡히지 않았다. 애초에 JVM에 대해 아는 것도 전혀 없어서 이참에 공부를 좀 해봐야겠다고 생각했다. 드디어 경각심을 가지게 된 감자 🥔
이렇게 엄청난 구조로 이루어져 있는데, Method Area와 Heap Area 두 개의 영역만 모든 스레드에서 공유된다.
프로세스 영역
Method Area
메서드 영역에는 인스턴스 생성을 위한 클래스 정보, 메서드 데이터, static 변수 등이 저장된다.
Heap
힙 영역에는 코드 실행을 위한 인스턴스, 배열 등이 저장된다. 또한 참조되지 않는 인스턴스와 배열에 대한 정보를 얻을 수 있는 영역이기에 가비지 컬렉터의 주 대상이 된다.
스레드 영역
PC (Program Counter)
개별적으로 메서드를 실행하는 스레드들을 위해, 현재 실행되고 있는 명령어의 주소 저장하는 역할이다.
Native Method Stacks
JVM이 성능 향상을 목적으로 Java 바이트코드가 아닌 다른 언어(C나 C++)로 작성된 메서드를 호출하기 위해 필요한 정보를 저장한다.
Java Stacks
Java Stack은 스레드 별로 한 개씩 존재한다. Java Stack은 메서드를 실행하기 위한 정보들이 저장되는 공간으로, Frame이라는 자료구조가 쌓이는 방식이다. 메서드가 하나 실행될 때마다 하나의 프레임이 스택에 쌓이고, 메서드가 종료되거나 예외가 발생하면 순차적으로 스택에서 제거된다.
Stack Frame 구성 요소
- Local Variables Array: 해당 메서드의 모든 매개변수와 지역 변수가 저장되는 곳이다.
- Operand Stack: 연산에 필요한 피연산자와 연산 결과를 저장하고 관리하는 스택으로, 메서드가 실행될 때 수행되는 모든 연산은 이 Operand Stack을 통해 처리된다.
- Frame Data
- Constant Pool Reference: 현재 메서드에서 사용되는 상수, 메서드 및 필드 참조에 대한 정보를 제공한다.
- Return Address: 메서드가 종료되었을 때 복귀해야 하는 주소 정보를 저장한다.
- Exception Information: 현재 메서드와 관련된 예외 처리 정보가 포함된다.
Java Stack 동작 예시
public class Example {
public static void main(String[] args) {
int a = 10; // 1
int b = 20; // 2
int sum = add(a, b); // 3
}
public static int add(int x, int y) {
return x + y; // 4
}
}
- main 메서드가 호출될 때 main 메서드의 스택 프레임이 Java Stack에 생성된다.
- main 프레임의 Local Variables Array에는 args, a, b 등이 저장된다.
- add(a, b) 호출 시 add 메서드에 대한 새로운 스택 프레임이 생성된다.
- add 메서드 프레임 내에서 x, y가 Local Variables Array에 저장되고, x + y 연산을 수행하기 위해 Operand Stack에 x, y가 push된다.
- 연산 결과가 다시 Operand Stack에 push되고 return 구문을 통해 main 프레임으로 제어가 넘어가면, add 프레임은 스택에서 제거된다.
📌 어렵다 어려워
공부를 하다보니 스레드 간 공유되는 영역보다 Java Stacks에 대한 학습을 더 많이 한 것 같기도 한데 😓 아무튼 동시성 확장한다고 스레드 풀을 활용할 때는 Method Area와 Heap Area에 저장된 데이터를 신경 써서 관리해 주면 될 것 같다. 동시성 문제를 해결하는 방법에 대한 글도 써야 하는데 내가 언제가 써주겠지...? 👀
https://www.geeksforgeeks.org/java-virtual-machine-jvm-stack-area/
https://www.youtube.com/watch?v=GU254H0N93Y
https://www.baeldung.com/java-stack-heap
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html
https://www.javatpoint.com/memory-management-in-java
https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/
https://coderstea.in/post/java/get-ready-to-deep-dive-java-memory-management-structure-of-jvm/
'Java' 카테고리의 다른 글
[Java] Enum을 비교해 보자 (56) | 2024.04.15 |
---|---|
[Java] String.matches() 대신 Pattern을 사용하자 (12) | 2024.03.17 |