728x90
반응형
자바 개발자는 가상 머신이 제공하는 자동 메모리 관리 메커니즘 덕에 메모리 할당과 해제를 코딩하지 않아도 메모리 누수나 오버플로 문제를 거의 겪지 않음
하지만 통제권을 위임했기 때문에 문제가 한 번 터지면 가상 머신의 메모리 관리 방식을 이해하지 못하는 한 해결하기가 상당히 어려움
런타임 데이터 영역
자바 가상 머신은 자바 프로그램을 실행하는 동안 메모리를 몇 개의 데이터 영역으로 나누어 관리하며 영역별로 목적과 생성/삭제 시점이 있음
프로그램 카운터
PC 레지스터는 작은 메모리 영역이지만, JVM의 멀티스레딩 구현과 프로그램 실행 흐름 제어에 핵심적인 역할을 함
- 작은 메모리 영역으로 현재 실행 중인 스레드의 바이트 코드 줄 번호를 가르키는 표시기
- 각 스레드마다 별도의 스택 보유(스레드 프라이빗 메모리)
- 스레드와 동일한 라이프사이클(스레드가 생성될 때 함께 생성되고, 종료되면 함께 소멸)
주요 기능
- 실행 흐름 제어
- 바이트코드 인터프리터는 PC의 값을 변경하여 다음에 실행할 명령어를 결정
- 프로그램의 제어 흐름, 분기, 순환 점프 등을 표현
- 스레드 컨텍스트 관리
- 멀티스레딩 환경에서 각 스레드의 실행 상태를 추적
- 스레드 전환 후 이전에 실행하던 지점을 정확히 복원하는데 필요
- 예외 처리 지원
- 예외 발생 시 어떤 코드에서 예외가 발생했는지 추적
- 예외 처리기로 제어를 전달하기 위한 정보 제공
특징
- 스레드가 자바 메서드를 실행 중일 때, 실행 중인 바이트코드 명령어의 주소가 기록됨
- 스레드가 네이티브 메서드를 실행 중일 때, PC 값은 Undefined 상태가 됨
- 각 스레드의 PC는 서로 독립적으로 동작하며 다른 스레드의 PC에 영향을 주지 않음
스레드 프라이빗 메모리
- 멀티스레딩 환경에서 CPU 코어가 여러 스레드를 번갈아 실행할 때 컨텍스트 스위칭을 정확히 수행하기 위한 핵심 요소
- 스레드 간 메모리 격리를 통해 안전한 멀티스레딩 실행 보장
자바 가상 머신 스택
- 각 스레드마다 별도의 스택 보유(스레드 프라이빗 메모리)
- 스레드와 동일한 라이프사이클(스레드가 생성될 때 함께 생성되고, 종료되면 함께 소멸)
- 자바 메서드 실행을 위한 메모리 모델
동작 원리
- 메서드 호출 시, 새로운 스택 프레임을 생성하여 스텍에 push
- 메서드 종료 시, 해당 스택 프레임을 스택에서 pop
- LIFO(Last-In-First-Out) 구조로 동작
스택 프레임 구성 요소
- 지역 변수 테이블(Local Variable Table)
- 컴파일 타임에 결정되는 기본 데이터 타입, 객체 참조, 반환 주소 타입 저장
- 지역 변수 슬롯으로 구성
- 일반적으로 슬롯 하나의 크기는 32비트
- double, long 같은 64비트 데이터는 슬롯 두 개 차지
- 필요한 슬롯 개수는 컴파일 시점에 결정되며 실행 중에는 변하지 않음
- 실제 메모리 크기는 JVM 구현에 따라 달라질 수 있음
- 피연산자 스택(Operand Stack)
- 메서드 내 연산을 위한 작업 공간
- 바이트코드 명령어가 작업을 수행하기 위한 데이터를 저장하고 결과를 보관
- 동적 링크(Dynamic Linking)
- 현재 메서드에서 사용하는 클래스와 메서드에 대한 참조 정보
- 메서드 반환값(Return Value)
- 메서드 실행 완료 후 호출자에게 전달할 값 저장
발생 가능한 오류
- StackOverflowError:
스레드가 요청한 스택 깂이가 JVM이 허용하는 깊이를 초과할 때 발생.
주로 과도한 재귀 호출이나 메서드 호출 중첩에서 발생 - OutOfMemoryError:
스택을 동적으로 확장할 수 있는 JVM에서 스택 확장 시 메모리가 부족할 때 발생
주의사항
- 자바 메모리 구조를 단순히 '힙과 스택'으로 구분하는 것은 정확하지 않음
- JVM 메모리 구조는 더 복잡하며, 스택은 그중 한 부분임
네이티브 메서드 스택
JVM이 자바 외의 언어로 작성된 코드와 상호작용할 수 있게 해주는 메모리 영역
- 네이티브 메서드(JNI: Java Native Interface를 통해 호출되는 C/C++ 등의 코드)를 실행하기 위한 스택
- 자바 가상 머신 스택과 유사한 역할을 수행하지만 다른 대상을 위해 사용됨
- 스레드 프라이빗 메모리
구현의 자유도
- JVM 명세는 네이티브 메서드 스택의 내부 구조에 대해 특별히 명시하지 않음
- 구현자가 원하는 방식으로 자유롭게 구현 가능
- 일부 JVM 구현체(핫스팟 포함)는 가상 머신 스택과 네이티브 메서드 스택을 하나로 통합
발생 가능한 오류
- StackOverflowError: 허용 깊이를 초과할 때 발생
- OutOfMemoryError: 스택을 동적으로 확장하려 할 때 메모리가 부족한 경우 발생
네이티브 메서드 사용 사례
- 하드웨어 접근이 필요한 경우
- 플랫폼 종속적인 기능을 사용해야 할 때
- 이미 C/C++로 작성된 라이브러리를 재사용할 때
- 성능이 중요한 특정 연산을 최적화할 때
자바 힙
- 자바 애플리케이션이 사용할 수 있는 가장 큰 메모리 영역
- 모드 스레드가 공유하는 공통 메모리 영역
- JVM이 시작될 때 생성됨
- 객체 인스턴스의 저장소 역할 수행. 가비지 컬렉션의 주요 대상
주요 특징
- 목적과 용도
- 객체 인스턴스를 저장하는 것이 주요 목적
- 거의 모든 객체 인스턴스가 이 영역에 할당됨 (자바 언어 발전에 따라 모든 객체가 할당된다고 볼 수 없음)
- 가비지 컬렉션(GC)
- 가비지 컬렉터가 관리하는 메모리 영역으로 GC 힙 이라고도 불림
- 전통적으로 세대별 컬렉션 이론(Generational Collection Theory)에 기반하여 설계됨
- 신세대, 구세대, 에덴 공간, 생존자 공간.. 이런 개념
- 최신 GC 알고리즘은 전통적인 세대 구분을 따르지 않을 수 있음
- 메모리 할당
- 모든 스레드가 공유하지만, 할당 효율을 높이기 위해 스레드 로컬 할당 버퍼(TLAB, Thread-Local Allocation Buffer)로 나뉨
- 어떻게 세부 영역이 나뉘든 모든 객체 인스턴스는 결국 자바 힙에 저장됨
- 세부 영역 구분은 메모리 회수와 할당 속도 향상이 주목적
- 메모리 구조
- 논리적으로는 연속된 메모리로 취급, 물리적으로는 떨어진 메모리에 위치해도 상관없음
- 단, 대부분 JVM 구현체는 큰 객체(특히 배열)의 저장 효율과 구현 로직 단순화를 위해 물리적으로도 연속된 메모리 공간을 사용
- 크기 설정
- 고정 크기 또는 가변 크기로 설정 가능
- JVM 실행 옵션으로 초기 크기(`-Xms`)와 최대 크기(`-Xmx`) 지정 가능
발생 가능한 오류
- OutOfMemoryError: 새 인스턴스를 할당할 힙 공간이 부족하고 더 이상 확장할 수 없을 때 발생
메서드 영역
- 모든 스레드가 공유하는 메모리 영역
- JVM이 시작될 때 생성, 종료될 때까지 유지
- 클래스 로더가 로드한 클래스와 인터페이스의 메타데이터를 저장하는 물리적 메모리 영역
저장 정보
- 클래스와 인터페이스의 타입 정보
- 상수 풀(Constant Pool) 데이터
- 정적 변수(Static Variables)
- JIT 컴파일러가 최적화된 코드 캐시
- 메서드와 생성자 코드 등
메서드 영역과 힙의 관계
- 논리적으로 힙의 일부로 기술되지만 구분을 위해 '논힙(non-heap)'이라고도 불림
- 메서드 영역은 자바 힙과 구조적으로나 목적상으로 다름
구현 변천사 (HotSpot VM 기준)
- JDK 7 이전
- 메서드 영역을 '영구 세대(PermGen)'에 구현
- 가비지 컬렉터의 수집 범위를 메서드 영역까지 확장
- 영구 세대에서 자바 힙처럼 메서드 영역 관리
- 메모리 오버플로 가능성 높음
- JDK 7
- 문자열 상수와 정적 변수 등 일부 정보를 자바 힙으로 이동
- 네이티브 메모리에 구현하는 계획 시작
- JDK 8 이후
- 영구 세대 개념 제거
- 메서드 영역을 네이티브 메모리에 '메타스페이스(Metaspace)'로 구현
- 시스템 메모리를 활용하여 자동으로 크기 조정 가능
구현 특성
- 물리적으로 연속될 필요 없음
- 고정 크기로 설정하거나 확장 가능하게 구현 가능
- 가비지 컬렉션을 수행하지 않아도 됨(필수 아님)
- 회수 대상은 주로 상수 풀과 타입 정보
- 회수 효과가 상대적으로 작고 조건이 까다로움
- 그러나 특정 상황에서는 회수가 필요할 수 있음
발생할 수 있는 오류
- OutOfMemoryError: 메서드 영역이 가득 차서 필요한 메모리를 할당할 수 없을 때 발생
JDK 8 이후에는 `-XX:MaxMetaspaceSize` 옵션으로 메타스페이스 최대 크기 제한 가능
런타임 상수 풀
- 메서드 영역의 일부
- 클래스나 인터페이스가 JVM에 로드될 때 생성됨
- 클래스 파일의 상수 풀 테이블에 있는 정보를 런타임에 사용하기 위한 형태로 저장
- JVM이 프로그램을 실행하는 데 필요한 다양한 상수 정보를 관리하는 중요한 메모리 영역으로, 자바의 동적 특성을 지원하는 핵심 요소
저장 정보
- 클래스 메타데이터(버전, 필드, 메서드, 인터페이스 정보 등)
- 컴파일 타임에 생성된 리터럴(숫자 상수, 문자열 리터럴 등)
- 심벌 참조(Symbolic References)
- 클래스와 인터페이스 이름
- 필드 이름과 설명자
- 메서드 이름과 설명자
- 심벌 참조에서 번역된 직접 참조(Direct References)
특징
- 동적 특성
- 클래스 파일의 상수 풀과 달리 런타임에도 동적으로 내용이 변경될 수 있음
- 런타임 중에 새로운 상수가 추가될 수 있음
- String 클래스의 intern() 메서드를 통한 문자열 상수 풀 추가
- 구현의 자유도
- JVM 명세는 런타임 상수 풀에 대해 상세한 구현 요구사항을 정의하지 않음. 자유롭게 구현 가능
- 일반적으로 심벌 참조와 직접 참조 모두 런타임 상수 풀에 저장됨
- 메모리 제약
- 메서드 영역에 속하므로 메서드 영역의 공간 제약을 받음
- OutOfMemoryError: 상수 풀 공간이 부족하면 발생 가능
역할과 중요성
- 클래스 로딩과 링킹 과정에서 중요한 역할 수행
- 컴파일 타임에는 알 수 없는 참조를 런타임에 해석하는 데 필요
- 다양한 타입의 사수를 통합 관리하여 효율적인 메모리 사용 지원
- 동적 바인딩(Dynamic Binding)을 가능하게 함
다이렉트 메모리
- JVM 런타임 데이터 영역에 공식적으로 포함되지 않는 메모리 영역
- 운영체제의 네이티브 메모리를 직접 사용하는 영역
- JDK 1.4부터 도입된 NIO(New I/O) 라이브러리와 함께 사용되기 시작
- 대용량 데이터 처리나 고성능 I/O 작업에 유용하지만, 명시적인 메모리 관리와 시스템 전체 메모리 사용량에 대한 이해가 필요한 영역
NIO와 다이렉트 메모리
- NIO는 채널(Channel)과 버퍼(Buffer) 기반의 I/O 메서드 제공
- `DirectByteBuffer` 객체를 통해 네이티브 메모리에 직접 접근
- 자바 힙과 네이티브 힙 사이의 데이터 복사 과정을 생략하여 성능 향상
- 물리 메모리를 직접 할당하기 때문에 I/O 작업 성능 개선
특성
- 메모리 제약
- 자바 힙 크기의 제약과 무관하게 사용 가능
- 하지만 시스템 물리 메모리 총량과 프로세서가 다룰 수 있는 주소 공간 제약을 받음(물리 메모리, 스왑 파티션, 페이징 파일)
- 메모리 관리
- 자바 GC의 직접적인 관리 대상이 아님
- `DirectByteBuffer` 객체는 힙에 할당되지만, 실제 버퍼 데이터는 네이티브 메모리에 저장
- `DirectByteBuffer` 객체가 GC에 의해 수집될 때 네이티브 메모리도 함께 해제
- 성능 특성
- 네이티브 I/O 작업에 최적화됨
- 자바 힙과 네이티브 메모리 간 데이터 복사 비용 절감
- 큰 데이터 처리 시 성능 이점이 있음
메모리 관리 주의사항
- 서버 관리자들이 JVM 힙 메모리만 고려하고 다이렉트 메모리를 간과하는 경우가 많음
- OutOfMemoryError: 다이렉트 메모리 사용량이 제한을 초과하면 발생 가능
- `-XX:MaxDirectMemorySize` 옵션으로 최대 다이렉트 메모리 크기 설정 가능
- 전체 시스템 메모리 사용량(JVM 힙 + 다이렉트 메모리 + 기타 네이티브 메모리)이 물리적 제약을 초과하지 않도록 주의해야 함
728x90
반응형
'📙 > Study' 카테고리의 다른 글
[JVM 파헤치기] 02. 자바 메모리 영역과 메모리 오버플로(2) (0) | 2025.03.24 |
---|---|
[JVM 파헤치기] 01.자바 기술 시스템 소개(2) (0) | 2025.03.18 |
[JVM 파헤치기] 01. 자바 기술 시스템 소개(1) (1) | 2025.03.17 |
[만들면서 배우는 클린 아키텍처] 12. 아키텍처 스타일 결정하기 (1) | 2024.10.20 |
[만들면서 배우는 클린 아키텍처] 11. 의식적으로 지름길 사용하기 (1) | 2024.10.19 |