2. 자바 메모리 영역과 메모리 오버플로
2.2 런타임 데이터 영역
1. 프로그램 카운터 레지스터 PC
- 현재 실행중인 스레드의 바이트 코드 줄 번호 표시기
- 스레드 프라이빗 영역
- OOM 조건이 명시되지 않은 유일한 영역.
- CPU 코어가 여러 스레드를 동시에 실행할때, 시분할로 실행되므로, 스레드 전환 후 이전에 실행하다 멈춘 지점을 정확하게 복원하려면 스레드 각각의 pc 가 필요하다.
- 스레드가 네이티브 메서드를 실행중일때 프로그램 카운터 값은 Undefined 이다.
2. 자바 가상 머신 스택
- 가상머신 스택 : stack frame 을 stack으로 관리한다.
- stack frame : 메서드가 실행될때마다 지역 변수 테이블, 피연산자 스택, 동적 링크, 메서드 반환값 등 정보를 저장한다.
- 스레드 프라이빗 영역
- StackOverFlow, OOM 발생가능.
지역변수 테이블
- 컴파일 타임에 알 수 있는 데이터 타입, 객체 참조, 반환 주소 타입 저장.
- 지역 변수 슬롯 : 일반적으로 슬롯 하나당 32 bit 이며, double 타입은 두개의 슬롯을 차지한다.
- 컴파일 과정에서 지역변수테이블의 크기는(슬롯 갯수는) 할당된다.
3. 네이티브 메서드 스택
- 가상머신 스택과의 차이는 네이티브 메서드를 실행할 때 사용한다는 것.
- 스레드 프라이빗 영역
- StackOverFlow, OOM 발생가능.
4. 자바 Heap (= GC Heap)
- 모든 스레드가 공유하는 영역
- 객체 인스턴스를 저장하는 곳
- GC가 관리하는 메모리영역이므로, GC Heap 이라고도 불린다.
- 자바 힙을 다시 작게 구분하는 목적은 메모리 회수와 할당을 더 빠르게 하기 위함.
- new generation, old generation, 영구세대, eden space,.. 다음 장에서 더 상세히 다룸.
- Xmx, Xms 로 크기 조정 가능.
- OOM 발생 가능
5. 메서드 영역 (= non Heap)
- 모든 스레드가 공유한다.
- VM이 읽어들인 타입정보, 상수, 정적변수, JIT 컴파일러가 컴파일한 코드 캐시 등 저장.
- JVM7 까지 메서드 영역까지 GC 대상으로 두었던 시절엔 힙메모리의 영구세대에 구현되어있었다.
- JVM8 부터 MetaSpace 영역으로, 힙메모리에서 분리되어 OS가 관리하는 네이티브 메모리에 올라간다. (2.4.3 참고)
- OOM 발생 가능.
5.1 런타임 상수풀
- 상수 풀 테이블에는 클래스 버전, 필드, 메서드, 인터페이스 등 클래스 파일에 포함된 설명 정보 + 컴파일 타미에 생성된 다양한 리터럴과 심벌 참조.
- 클래스를 로드할때 올림.
- 클래스파일의 상수풀과 비교하여 동적이다. 런타임에도 메서드 영역의 런타임 상수풀에 새로운 상수가 추가될 수 있다.
- JVM7 부터 힙메모리에 위치한다. (2.4.3 참고)
- ex) String.intern()
7. 다이렉트 메모리
- 가상머신 런타임에 속하지 않음.
- NIO (NotI/O) 는 힙이 아닌 메모리를 직접 할당할 수 있는 네이티브 함수 라이브러리를 사용함.
- 물리 메모리 한계를 넘으면 OOM 발생 가능.
2.3 핫스팟 가상머신에서의 객체 들여다보기
new 객체 생성시
바이트코드 단계
- new : 메모리 할당, JVM 관점에서 객체 생성
- invokespecial : 생성자 실행 , 자바 프로그램 관점에서 객체 생성
메모리 할당 방식
- 포인터 밀치기 할당방식
- 그림 2.2 참고
- 사용중인 메모리 뒤에 객체를 할당하여 포인터가 증가되는 방식
- 자바 힙이 규칙적이다 = GC가 compact(모으기)를 할 수 없다. - 여유 목록 (free list)
- 객체 인스턴스를 담기에 충분한 공간을 찾아 할당 후 목록을 갱신.
- 자바 힙이 불규칙적이다 = GC가 compact(모으기)를 할 수 있다.
메모리 할당 동기화 방식
- 멀티 스레딩 환경에서는 메모리 포인터 수정이 위험할 수 있다.
- 갱신을 원자적으로 수행. (단일 스레드가 메모리 할당을 처리)
- 스레드 로컬 할당 버퍼 TLAB
- XX:+/-UseTLAB
- 스레드마다 다른 메모리 공간을 미리 할당받아놓는방식.
2.3.2 객체의 메모리 레이아웃
- 그림 2-4 참고
객체 헤더
- mark word
- 32bit or 64bit, 8byte 단위로 고정됨.
- 객체 해시코드, GC 세대 나이, 락 상태 플래그, 스레드가 점유하고있는 락들, 편향된스레드 아이디, 편향된 시각의 타임스탬프 등..
편향되었다는게 무엇을 의미하는지?
- Klass word
- 객체의 클래스 관련 메타데이터를 가리키는 클래스 포인터.
- 객체가 배열인경우 원소의 타입이 저장된다.
- array length
- 배열인경우 배열 길이
인스턴스 데이터
: 객체가 실제로 담고있는 정보.
- 필드 순서 할당 전략
-XX:FieldsAllocationStyle기본 : 길이가 같은 필드들은 항상 같이 할당되고 저장된다.-XX:CompactFields길이가 짧은 필드를 상위 클래스 변수 사이에 배치하여 공간절약.왜 기본전략인건지? CompactFields 의 단점은?
정렬 패딩
: 인스턴스데이터가 8byte단위로 끊기지 않을 수 있으므로, 8btye를 맞춰주기위한 패딩
2.3.3 객체에 접근하기
- 그림 2.5, 2.6 참고
핸들방식
- heap 에 핸들풀과 인스턴스풀이 존재하며, 핸들풀에는 객체의 참조 포인터만 존재(인스턴스 데이터 포인터, 타입 데이터 포인터)
- 장점 : GC 과정에서 객체 인스턴스의 위치이동이 빈번한데, 위치이동이 되더라도 핸들풀의 참조는 변경하지 않아도 된다.
다이렉트 포인터
- 핫스팟 JVM 의 기본 전략
- heap 에 풀이 존재하지 않으며, 참조포인터와 데이터가 한묶음.(인스턴스 데이터, 타입 데이터 포인터)
- 장점 : 속도
2.4 실전:OutOfMemoryError 예외
2.4.1 자바 힙 오버플로우
- 힙덤프 로 메모리 분석하여 메모리 누수를 확인한다.
- 메모리 누수가 아니라면
- 수명주기가 너무 긴 객체
- 공간낭비가 심한 데이터 구조인지
- Xmx, Xms 값이 적절한지
2.4.2 가상머신 스택과 네이티브 메서드 스택 오버플로우
- 스레드가 요구하는 스택의 깊이 > VM이 허용하는 최대 깊이 : StackOverFlow
- VM이 스택 메모리를 동적으로 확장하는 기능을 지원하지만, 가용 메모리가 부족한 경우 : OutOfMemoryError
- HotspotVM 은 확장을 지원하지 않는다.
- 스레드에 할당가능한 메모리 예
- 32bit window 의 프로세스당 최대 메모리는 2GB
가상 머신 스택+네이티브 메서드 스택메모리 =프로세스당 최대 매모리-Xmx 최대 힙메모리-XX:metaSpace 최대 메서드영역 메모리- 더 많은 스레드를 할당하고싶다면, 최대 힙크기를 줄이거나, 스레드당 스택크기를 줄이는 방법이 있다.
2.4.3 메서드 영역과 런타임 상수 풀 오버플로우
- String::intern()
- 문자열 상수풀에 같은 값이 있다면 참조를 반환, 없다면 상수풀에 추가 후 반환.
- 런타임에 상수풀에 새로운값을 추가할 수 있는 방식.
- 문자열 상수풀에 같은 값이 있다면 참조를 반환, 없다면 상수풀에 추가 후 반환.
- ~JVM6 : 상수풀이 영구세대에 있었음 (XX:PermSize)
- JVM7~ : 상수풀이 힙메모리에 있음.
- JVM8~ : 영구세대가 사라지고, 메타스페이스가 대체.
출처 : https://www.programmersought.com/article/4905216600/
- JVM별 실험 1
while(true){ collection.add(String.valueOf(i++).intern()); }- ~JVM6 : OOM : PermGen Size 에러 반환
- JVM7~ : OOM : Java heap Size 에러 반환
- JVM별 실험 2
String str1 = new StringBuilder("aa").toString() System.out.println(str1.intern() == str1)- ~JVM6 : false, 다른 영역에 있으므로.
- JVM7~ : true, 같은 영역에 있으므로.
2.4.4 : 네이티브 다이렉트 메모리 오버플로우
- default : XX:MaxDirectMemorySize = Xmx value
- 다이렉트 메모리에서 발생한 오버플로우는 힙덤프 파일에서는 이상한 점을 찾을 수 없다.