일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 결제서비스
- spring event
- 카카오
- gRPC
- 레디스 동시성
- jwt 표준
- branch 전략
- 이분탐색
- 트랜잭샨
- 구현
- 백준
- 누적합
- 검색어 추천
- 좋은 코드 나쁜 코드
- 수신자 대상 다르게
- 쿠키
- 숫자 블록
- 디버깅
- BFS
- 객체지향패러다임
- AWS
- 알람 시스템
- 코드 계약
- 셀러리
- docker
- 깊게 생각해보기
- prg 패턴
- 프로그래머스
- 완전탐색
- piplining
- Today
- Total
코딩관계론
JVM 메모리 구조 본문
이전 시간에는 JVM이 코드를 어떤 방식으로 실행하는지 알아봤다(2024.06.02 - [개발/Java] - 자바 어떻게 실행되는가?)
지금부터는 JVM의 데이터 영역에 대해서 집중적으로 탐구해보겠다.
JVM의 Run time data area 영역의 실행주기는 JVM의 시작과 종료, 각각의 thread 시작과 종료에 따라 나눌 수 있다.
thread의 생명 주기에 따라 생성과 소멸하는 데이터 영역을 먼저 확인해보자.
PC Registers
JVM은 여러 개의 쓰레드가 생성될 수 있고, 해당 쓰레드는 명령어를 실행중인데, 해당 명령어의 주소를 저장하는 영역입니다. 자바의 가상 머신은 여러개의 thread를 생성할 수 있고, 동시에 실행할 수 있기 때문에 각 thread는 고유한 프로그램 카운터 레지스터를 가지고 있습니다.
항상 이 방식으로 동작하는 것은 아닙니다. 아래의 영어 원문을 확인하시면 "native 메소드를 실행중vs 아니냐"에 따라서 PC register에 값이 있을 수도 있고 없을 수도 있다라고 설명합니다.
If the method currently being executed by the thread is native, the value of the Java Virtual Machine's pc register is undefined.
네이티브 메소드는 Java로 작성되지 않은 코드입니다, 따라서 호출은 가능하지만 해당 메소드가 실행 중일 때 JVM은 그 내부 동작을 추적할 수 없기 때문에 PC레지스터의 값을 정의할 수 없습니다. PC레지스터의 주소 값은 매우 크기 때문에 아키텍쳐가 달라져서 PC레지스터의 주소 값이 잘못되는 일은 없습니다.
Java Virtual Machine Stacks(Java Stack)
Java Virtual Machine Stack은 frame을 저장하기 위해서 사용됩니다. 스택의 동작 과정은 아래와 같습니다.
자바 가상 머신 스택에서 프래임을 팝하거나 푸시하는것 말고는 직접 조작되지 않습니다.
stack의 경우 두 가지 오류가 발생할 수 있습니다.
1. OutOfMemory
이 오류는 Thread를 생성할 떄 스택을 할당하게 되는데 요청된 스택의 크기가 할당할 수 있는 스택보다 크다면 발생하는 에러입니다.
2.StackOverFlow
이 오류의 경우에는 Stack의 크기가 고정되어 있고, 해당 스택에 프레임을 할당할 수 없을 때 발생되게 됩니다.
이 경우는 무한 재귀 호출을 예로 들 수 있습니다.
Frame이란?
프레임은 메소드 호출에 필요한 데이터 및 부분 결과를 저장하는데 사용됩니다. 또한 동적 링킹, 메서드의 반환 값, 예외 처리 등의 작업을 수행합니다. 이는 각 메소드 호출의 실행 컨텍스트를 유지하고 관리하는 데 중요한 역할을 합니다.
생명주기는 메서드가 호출될 때는 새로운 프레임이 생성되어 새로운 메서드로 제어가 전환됩니다. 메서드 반환 시 현재 프레임은 해당 메서드 호출 결과를 이전 프레임에게 반환하고 이전 프레임이 현재 프레임이 되면 현재 프레임은 파괴됩니다.
현재 프래임이란 주어진 제어 스레드에서는 한 번에 하나의 프레임만 활성화됩니다. 이 프레임은 현재 프레임이라고 하며, 해당 메서드는 현재 메서드로 알려져 있습니다. 현재 메서드가 정의된 클래스는 현재 클래스입니다.
중요한 점은 한 스레드에서 생성된 프레임은 해당 스레드에 로컬이며 다른 스레드에서 참조될 수 없습니다.
Native Method Stack
네이티브 메서드 스택은 Java 언어가 아닌 다른 언어로 작성된 네이티브 코드를 실행하는 데 사용됩니다. 이러한 네이티브 코드는 Java 프로그램에서 native 키워드로 선언된 메서드를 포함할 수 있습니다. 네이티브 메서드 스택은 JVM에서 네이티브 코드의 호출 및 실행을 관리합니다.
네이티브 메서드 스택의 크기는 JVM의 설정에 따라 다를 수 있으며, 일반적으로 네이티브 메서드 호출의 깊이에 따라 동적으로 조정됩니다. 그러나 네이티브 메서드 스택이 필요한 크기를 초과하거나 운영 체제가 충분한 메모리를 제공하지 않을 경우에는 OutOfMemoryError가 발생할 수 있습니다.
네이티브 메서드 스택은 주로 성능 향상이 필요한 부분에서 사용됩니다. 예를 들어, 네이티브 라이브러리를 사용하여 하드웨어 접근이나 네트워크 통신과 같은 작업을 수행할 때 자바의 가상 머신보다 빠른 속도를 얻을 수 있습니다. 그러나 네이티브 메서드를 사용하는 것은 자바의 이식성을 저하시킬 수 있으므로 주의가 필요합니다.
위의 세 개는 Thread의 생명 주기에 따라서 생성되고 소멸되는 데이터 영역입니다. 다음으로는 JVM의 생명주기와 같은 데이터 영역을 알아보겠습니다.
Heap
힙은 자바 프로그램에서 동적으로 할당되는 객체들의 메모리 영역입니다. 이 영역은 JVM의 런타임 데이터 영역 중 하나로, 다른 데이터 영역인 스택과는 달리 객체 및 배열이 할당됩니다.
힙은 크게 세 영역으로 나뉩니다.
- Young Generation (Young 영역): 새롭게 생성된 객체들이 할당되는 영역입니다. 이 영역은 Eden 영역과 두 개의 Survivor 영역으로 나뉩니다. 새로운 객체는 처음에는 Eden 영역에 할당되며, 이후에는 Eden에서 살아남은 객체는 하나의 Survivor 영역으로 이동하게 됩니다. 주기적으로 GC가 실행되면, Eden 영역에 있는 객체들 중 살아남은 객체들이 다른 Survivor 영역으로 이동하거나 Old 영역으로 이동합니다.
- Old Generation (Old 영역 또는 Tenured 영역): Young 영역에서 일정 시간 살아남아 있는 객체들이 이동하는 영역입니다. Old 영역은 주로 크기가 크거나 오래된 객체들이 할당됩니다. 이 영역은 상대적으로 GC가 더 드물게 실행됩니다.
- Perm (Permanent) 영역 또는 Metaspace (Java 8 이상): 클래스 메타데이터 및 상수 풀 등의 정보가 저장되는 영역입니다. Java 8 이상에서는 Perm 영역이 Metaspace로 대체되었습니다. Metaspace는 힙 영역이 아니지만, GC의 대상이 될 수 있습니다.
힙은 GC(Garbage Collection)에 의해 관리되며, 더 이상 사용되지 않는 객체들은 GC에 의해 회수됩니다. 객체는 명시적으로 소멸시킬 필요가 없으며, GC는 더 이상 참조되지 않는 객체를 인식하고 회수합니다.
힙은 고정 크기일 수도 있고, 계산에 따라 필요에 따라 확장될 수도 있으며, 더 큰 힙이 필요하지 않으면 축소될 수도 있습니다. 힙을 위한 메모리는 연속적일 필요가 없습니다. JVM 구현은 프로그래머나 사용자에게 힙의 초기 크기를 제어할 수 있는 기능뿐만 아니라, 힙이 동적으로 확장되거나 축소될 수 있는 경우 최대 및 최소 힙 크기를 제어할 수 있는 기능을 제공할 수 있습니다. 계산이 자동 저장 관리 시스템이 제공할 수 있는 것보다 더 많은 힙을 필요로 하는 경우, JVM은 OutOfMemoryError를 발생시킵니다.
Method Area
Method area는 논리적인 영역으로 PermGen이나 Metaspace가 물리적으로 구현하게 됩니다.
메서드 영역은 Java의 클래스 구조, 메서드 데이터, 정적 변수 및 상수 풀 정보를 저장합니다. JVM의 구현에 따라서 GC의 대상이 될 수도 있고, 아닐 수도 있습니다
메서드 영역의 크기는 고정될 수도 있고, 계산에 따라 동적으로 확장 또는 축소될 수도 있습니다. 메모리 할당은 연속적이지 않아도 됩니다. JVM 구현은 메서드 영역의 초기 크기뿐만 아니라, 가변 크기 메서드 영역의 최대 및 최소 크기에 대한 프로그래머 또는 사용자의 제어를 제공할 수 있습니다.
메서드 영역과 관련된 특이한 상황은 다음과 같습니다:
메서드 영역에서 메모리를 할당 요청을 충족할 수 없을 경우, 자바 가상 머신은 OutOfMemoryError를 throw합니다.
Run-Time Constant Pool
클래스 파일의 constant_pool 테이블의 런타임 표현으로, 클래스 또는 인터페이스마다 하나씩 존재합니다. 이 정보는 프로그램 실행 중에 사용되며, 자바 코드에서 참조될 때마다 사용됩니다.
PermGen vs Metaspace
PermGen의 경우 자바 8 이후로는 없어지고 Metaspace 영역이 생겨났습니다.
PermGen이 없어진 이유는 OutOfMemoryError(OOME)와 깊은 관련이 있습니다. PermGen은 고정된 크기를 가지며, 이를 초과할 경우 OutOfMemoryError가 발생했습니다. PermGen 영역이 없어지게 된 이유 중 하나는 클래스 로더가 많은 클래스를 로드하고 언로드하면서 메모리 누수가 발생하는 문제입니다. 특히, PermGen 영역에 저장된 상수 풀과 같은 데이터들이 GC에 의해 제대로 해제되지 않는 경우가 있었습니다.
이를 해결하기 위해 Metaspace가 도입되었습니다. Metaspace는 네이티브 메모리를 사용하고, 필요에 따라 메모리 크기를 자동으로 증가시킬 수 있어 PermGen에서 발생한 문제를 해결할 수 있었습니다.
*동적링킹
프로그램이 실행될 때, 프로그램이 사용하는 외부 라이브러리나 모듈을 실행 파일에 포함시키지 않고, 프로그램이 실행되는 도중에 필요한 라이브러리나 모듈을 불러오는 것을 말합니다. 이것은 프로그램의 유연성과 모듈화를 증가시키는 데 도움이 됩니다.
[자료조사]
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.6
'개발 > Java' 카테고리의 다른 글
Thread Safety하게 만들자 (0) | 2024.06.09 |
---|---|
static, final 어디까지 알아보고 왔는가? (0) | 2024.06.09 |
자바 어떻게 실행되는가? (0) | 2024.06.02 |
[실무 역량 과제] 신규 유형 (BE) (0) | 2024.05.30 |
Java에서 equals와 hashCode 메서드를 재정의해야 하는 이유 (0) | 2024.05.27 |