4. 영구 세대(PermGen)를 메타스페이스(MetaSpace)로 대체해야 하는 이유는 무엇입니까?
5. 힙 공간의 기본 구조를 이해하고 있나요? 어떤 상황에서 객체가 Old Generation에 들어가게 될까요?
6. 큰 개체는 어느 메모리 영역에 배치됩니까?
7. Java 객체 생성 과정은 어떻게 되나요?
질문 답변
1. JVM은 어떤 부분으로 구성되어 있나요?
답변: JVM은 실행 파일입니다.바이트코드 (.class) 파일 가상 컴퓨터 - 메모리 관리, 가비지 수집 및 기타 메커니즘도 제공합니다. 여기에는 다음과 같은 주요 부분이 포함되어 있습니다.
클래스 로딩 하위 시스템: 바이트코드 파일(.class)을 JVM에 로드하는 역할을 담당합니다.
런타임 데이터 영역: JVM이 실행 중에 사용하는 메모리 영역입니다.
실행 엔진: 프로세서 실행을 위해 바이트코드를 기계어 코드로 해석하거나 컴파일하는 역할을 담당합니다.
네이티브 라이브러리 인터페이스: 운영체제나 다른 언어로 작성된 네이티브 라이브러리를 호출하기 위한 API 세트를 제공합니다.
2. 런타임 데이터 영역에는 어떤 영역이 포함됩니까?
답변: 런타임 데이터 영역은 Java 프로그램을 실행할 때 JVM이 할당하는 메모리 영역입니다.
프로그램 카운터: 작은 메모리 공간으로 현재 스레드에서 실행 중인 바이트코드 명령어의 주소입니다. 스레드가 기본 메서드를 실행하는 경우 이 카운터의 값은 정의되지 않습니다.
Java 가상 머신 스택: 각 스레드는 생성될 때 스레드의 지역 변수 테이블, 피연산자 프레임, 동적 링크, 메소드 종료 정보 등을 저장하는 데 사용되는 가상 머신 스택을 생성합니다. Java 가상 머신 스택에는 여러 스택 프레임이 포함되어 있습니다. 호출되는 각 메소드에서 실행이 완료될 때까지의 프로세스는 스택 프레임을 가상 머신의 스택에 푸시하는 프로세스에 해당합니다.
네이티브 메소드 스택(Native method stack): 네이티브 메소드를 실행하기 위해 JVM이 마련한 공간으로, 자바 가상 머신 스택과 유사한 기능을 가지고 있으며 네이티브 메소드의 실행 과정을 기술하는 메모리 모델이다.
힙: 거의 모든 객체 인스턴스와 배열을 저장하는 데 사용되며 가비지 수집기가 작동하는 주요 영역입니다.
메소드 영역: JVM이 로드한 클래스 정보, 상수, 정적 변수, JIT(Just-In-Time) 컴파일러로 컴파일된 코드 등을 저장하는 데 사용됩니다. JDK1.8 이전에는 영구 세대로 구현되었습니다. JDK1.8부터 영구 생성이 원래 공간으로 대체됩니다. Metaspace는 힙 메모리 대신 로컬 메모리를 사용합니다.
3. 스택과 힙에는 각각 어떤 데이터가 저장되나요?
답변: 스택(Java Virtual Machine 스택)에 저장된 데이터는 다음과 같습니다.
지역 변수 테이블: 메소드 내에서 메소드 매개변수 및 지역 변수를 저장하는 데 주로 사용됩니다. 데이터 유형에는 기본 데이터 유형 및 객체 참조가 포함됩니다.
피연산자 스택: 메서드 실행 중 작업 지침과 중간 결과를 임시로 저장하는 데 사용됩니다.
동적 링크: 메서드에서 기호 참조를 확인하는 데 사용되는 메서드가 속한 클래스의 상수 풀에 대한 참조입니다.
메소드 반환 주소: 메소드 호출 이후 실행될 다음 명령어의 주소를 저장합니다. 힙에 저장된 데이터:
객체 인스턴스: 프로그램의 new 키워드를 통해 생성된 객체 인스턴스로, 객체의 속성과 메소드를 포함합니다.
배열: 기본 유형 배열 및 객체 배열을 포함한 모든 유형의 배열입니다.
4. 영구 세대(PermGen)를 메타스페이스(MetaSpace)로 대체해야 하는 이유는 무엇입니까?
답변: 영구 생성을 메타스페이스로 바꾸는 것은 주로 영구 생성의 몇 가지 고유한 문제와 제한 사항을 해결하고 JVM의 성능과 유연성을 향상시키는 것입니다.
메모리 관리의 유연성과 효율성 향상: 영구 생성의 메모리 크기는 JVM이 시작될 때 설정되며 동적으로 조정할 수 없습니다. Metaspace는 Java 힙 메모리 대신 로컬 메모리를 사용하며 필요에 따라 크기를 동적으로 조정할 수 있습니다.
클래스 언로드 및 가비지 수집 문제를 해결합니다. 영구 생성의 GC 동작은 복잡하고 예측할 수 없으며 재활용 효율성이 낮습니다.
더 나은 성능과 안정성 제공: 메타공간을 사용하면 다른 메모리 영역과 마찬가지로 로컬 메모리를 사용하여 관리되기 때문에 JVM 메모리 관리가 더욱 통합되고 일관됩니다. 이는 메모리 관리 전략을 단순화하고 전반적인 성능과 안정성을 향상시킵니다.
JVM 메모리 관리 단순화
5. 힙 공간의 기본 구조를 이해하고 있나요? 어떤 상황에서 객체가 Old Generation에 들어가게 될까요?
답변: 힙 공간의 기본 구조는 주로 신세대, 구세대, 영구 세대로 구성됩니다. JDK8 이후에는 영구 생성이 메타스페이스로 대체되고 저장을 위해 로컬 메모리를 사용합니다.
신생대 세대 : 신세대의 진행은 에덴 영역과 2개의 생존자 영역(생존자 0, 생존자 1)으로 세분화된다.
Eden 영역: 새로 생성된 객체는 먼저 Eden 영역에 메모리를 할당합니다.
생존 영역(S0, S1): 차세대 가비지 수집에서 살아남은 개체를 저장하는 데 사용됩니다. 각 Minor GC 후에 살아남은 객체는 이 두 영역 사이를 오가며 복사됩니다.
Old Generation: 여러 Minor GC 이후에도 여전히 살아있는 개체입니다. 가비지 수집(Major GC 또는 Full GC)은 이전 세대에서 덜 자주 수행됩니다.
영구 생성/메타공간: 클래스 정의, 상수, 정적 변수, JIT(Just-In-Time) 컴파일 코드 등을 포함하여 클래스의 메타데이터를 저장하는 데 사용됩니다.
객체가 Old Generation에 있을 때의 상황:
연령 임계값에 도달: 각 개체에는 새 세대에 메모리가 할당될 때 연령이 있으며 각 Minor GC 이후에 연령이 1씩 증가합니다. 연령이 특정 임계값(기본값은 15)에 도달하면 개체는 이전 세대로 승격됩니다.
대형 객체: 객체가 너무 크고 JVM에서 설정한 임계값을 초과하는 경우 객체는 이전 세대에서 직접 공간을 할당합니다.
생존자 영역의 공간 부족: 생존자 영역에 Minor GC 동안 살아남은 모든 개체를 수용할 공간이 충분하지 않은 경우 이러한 개체는
동적 객체 연령 결정: 생존자 공간에서 동일한 연령의 모든 객체 크기가 생존자 공간의 절반을 초과하는 경우 이 연령보다 크거나 같은 객체는 직접 Old Generation에 들어갈 수 있습니다.
// 动态年龄计算代码
uint ageTable::compute_tenuring_threshold(size_t survivor_capacity){//survivor_capacity是survivor空间的大小size_t desired_survivor_size =(size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);//TargetSurvivorRatio 为50size_t total =0;
uint age =1;while(age < table_size){
total += sizes[age];//sizes数组是每个年龄段对象大小if(total > desired_survivor_size)break;
age++;}
uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;...}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
6. 큰 개체는 어느 메모리 영역에 배치됩니까?
답변: 대형 객체(매우 큰 배열 및 문자열)는 일반적으로 Old Generation 메모리 영역에 직접 할당됩니다.이는 신세대가 잦은 공연을 하지 못하도록 하기 위함이다.쓰레기 수거이때 Eden 영역과 Survivor 영역 사이에 대형 객체가 자주 복사되므로 가비지 수집 효율성이 향상됩니다. 이전 세대에 직접 들어가도록 대형 개체에 대한 임계값을 구성합니다.