기술나눔

JVM 관련

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

JDK 아키텍처

Java 언어의 크로스 플랫폼 기능

JVM 전체 구조 및 메모리 모델

JVM 메모리 매개변수 설정

Spring Boot 프로그램의 JVM 매개변수 설정 형식(Tomcat 시작은 bin 디렉토리의 catalina.sh 파일에 직접 추가됩니다):

자바 -Xms2048M -Xmx2048M -Xmn1024M -Xss512K -XX:메타스페이스크기=256M -XX:최대메타스페이스크기=256M -jar 마이크로서비스-유레카-서버.jar

결론적으로:

-Xss 설정이 작을수록 카운트 값도 작아집니다. 즉, 스레드 스택에 할당할 수 있는 스택 프레임 수는 적지만 JVM 전체에서는 열 수 있는 스레드 수가 더 많아집니다.

-Xss: 각 스레드의 스택 크기, 기본값 1M

-Xms: 힙의 초기 사용 가능한 크기를 설정합니다. 기본값은 실제 메모리의 1/64입니다.

-Xmx: 사용 가능한 최대 힙 크기를 설정합니다. 기본값은 실제 메모리의 1/4입니다.

-Xmn: 차세대 크기

-XX:NewRatio: 기본값 2는 새로운 세대가 이전 세대의 1/2, 전체 힙 메모리의 1/3을 차지함을 의미합니다.

-XX:SurvivorRatio: 기본값 8은 생존자 영역이 Eden 메모리의 1/8, 즉 차세대 메모리의 1/10을 차지한다는 의미입니다.

메타스페이스에 대한 두 가지 JVM 매개변수가 있습니다: -XX:MetaspaceSize=N 및 -XX:MaxMetaspaceSize=N

-XX: MaxMetaspaceSize: 최대 메타공간 크기를 설정합니다. 기본값은 -1입니다. 이는 제한이 없거나 로컬 메모리 크기에 의해서만 제한된다는 의미입니다.

-XX: MetaspaceSize: Fullgc를 트리거할 메타스페이스의 초기 임계값을 바이트 단위로 지정합니다. 기본값은 약 21M입니다. 이 값에 도달하면 유형 언로드를 위해 전체 gc가 트리거됩니다. 수집기는 이 값을 조정합니다. 많은 양의 공간이 해제되면 값을 적절히 줄이고, 적은 양의 공간이 해제되면 -XX:MaxMetaspaceSize(설정된 경우)를 초과하지 않고 값을 적절하게 늘립니다. 이는 이전 jdk 버전의 -XX:PermSize 매개변수와는 다른 의미를 갖습니다. -XX:PermSize는 영구 생성의 초기 용량을 나타냅니다.

메타스페이스의 크기를 조정하려면 Full GC가 필요하므로 애플리케이션 시작 시 많은 수의 Full GC가 발생하는 경우 일반적으로 영구 생성 또는 메타공간의 크기를 조정하기 때문입니다. 일반적으로 JVM 매개 변수에서 MetaspaceSize와 MaxMetaspaceSize를 동일한 값으로 설정하고 초기 값보다 크게 설정하는 것이 좋습니다. 8G 물리적 메모리가 있는 머신의 경우 일반적으로 두 값을 모두 256M으로 설정합니다.

객체 생성

객체 헤더

HotSpot 가상 머신에서 메모리에 저장된 객체의 레이아웃은 객체 헤더(Header), 인스턴스 데이터(Instance Data), 정렬 패딩(Padding)의 세 가지 영역으로 나눌 수 있습니다. HotSpot 가상 머신의 개체 헤더에는 두 부분의 정보가 포함됩니다. 첫 번째 부분은 해시 코드(HashCode), GC 생성 기간, 잠금 상태 플래그, 스레드가 보유한 잠금과 같은 개체 자체의 런타임 데이터를 저장하는 데 사용됩니다. , 편향된 스레드 ID, 선호 타임스탬프 등 개체 헤더의 다른 부분은 개체의 클래스 메타데이터에 대한 포인터인 형식 포인터입니다. 가상 머신은 이 포인터를 사용하여 개체가 인스턴스인 클래스를 결정합니다.

32비트 개체 헤더

64비트 개체 헤더

포인터 압축

1. 64비트 플랫폼의 HotSpot에서 32비트 포인터(실제 저장소는 64비트 사용)를 사용하면 약 1.5배 더 많은 메모리를 사용하여 주 메모리와 캐시 간에 데이터를 이동합니다. 동시에 GC도 더 큰 압박을 받게 될 것입니다.

2. 64비트 플랫폼에서 메모리 소비를 줄이기 위해 포인터 압축 기능을 활성화합니다.

3. jvm에서 32비트 주소는 최대 4G의 메모리(2의 32승)를 지원하며, 이는 객체 포인터가 힙 메모리에 저장될 때 압축 및 인코딩하고, 꺼낸 후 디코딩하여 최적화할 수 있습니다. CPU 레지스터(객체 포인터는 힙에 있음 32비트, 레지스터에 35비트, 2의 35승 = 32G)로 jvm이 더 큰 메모리 구성(32G 이하)을 지원할 수 있음 32비트 주소

4. 힙 메모리가 4G 미만인 경우 포인터 압축을 활성화할 필요가 없습니다. jvm은 상위 32비트 주소를 직접 제거합니다. 즉, 하위 가상 주소 공간을 사용합니다.

5. 힙 메모리가 32G보다 크면 압축 포인터가 유효하지 않게 되고 64비트(즉, 8바이트)가 Java 객체를 주소 지정하도록 강제됩니다. 이로 인해 1의 문제가 발생하므로 사용하지 않는 것이 좋습니다. 힙 메모리가 32G보다 큽니다.

객체 스택 할당의 이스케이프 분석 및 제목 교체

이 경우 JVM에서는 Escape 분석 매개변수(-XX:+DoEscapeAnalytic)를 켜서 객체 메모리 할당 위치를 최적화할 수 있으므로 스칼라 대체(스택에서의 할당)를 통해 먼저 스택에 할당됩니다. JDK7 이후에는 기본적으로 켜져 있습니다. 원하는 경우 매개변수(-XX:-DoEscapeAnalytic)를 사용하여 꺼집니다.

스칼라 대체(Scalar replacement): 이스케이프 분석을 통해 객체가 외부에서 접근할 수 없고 객체를 더 분해할 수 있다고 판단되면 JVM은 객체를 생성하지 않고 객체 멤버 변수를 이 메서드에서 사용하는 여러 멤버 변수로 분해하고 이러한 대체 멤버 변수는 스택 프레임이나 레지스터에 공간을 할당하므로 연속된 공간이 크지 않아 개체에 대한 메모리 할당이 부족하지 않습니다. JDK7 이후 기본적으로 활성화되는 스칼라 대체 매개변수(-XX:+EliminateAllocations)를 활성화합니다.

스칼라 및 집계 수량: 스칼라는 더 이상 분해할 수 없는 수량이며 JAVA의 기본 데이터 유형은 스칼라(예: int, long 및 기타 기본 데이터 유형 및 참조 유형 등)입니다. 는 더 분해될 수 있는 양이고, 이 양을 중합량이라 한다. JAVA에서 객체는 더 분해될 수 있는 집합체입니다.

대형 객체는 Old Generation에 직접 입력됩니다.

대형 개체는 많은 양의 연속 메모리 공간(예: 문자열 및 배열)이 필요한 개체입니다. JVM 매개변수 -XX:PretenureSizeThreshold는 대형 개체의 크기를 설정할 수 있습니다. 개체가 설정된 크기를 초과하면 이전 세대에 직접 들어가고 젊은 세대에는 들어가지 않습니다. 이 매개변수는 Serial 및 ParNew 두 수집기에서만 유효합니다.

예를 들어, JVM 매개변수를 -XX:PretenureSizeThreshold=1000000(단위는 바이트) -XX:+UseSerialGC로 설정합니다. 위의 첫 번째 프로그램을 실행하면 대형 개체가 이전 세대에 직접 들어가는 것을 알 수 있습니다.

왜 이렇게 되어야만 하는가?

큰 개체에 메모리를 할당하고 효율성을 감소시킬 때 복사 작업을 방지하기 위해.

객체 동적 연령 판단

현재 객체가 위치한 Survivor 영역(오브젝트가 위치한 영역 중 하나인 s 영역)에서 객체 묶음의 전체 크기가 이 Survivor 영역의 메모리 크기(-XX)의 50%보다 큽니다. :TargetSurvivorRatio 를 지정할 수 있음) 이때 이 개체 배치의 최대 수명과 동일한 개체는 Old Age에 직접 들어갈 수 있습니다. age 1 + age 2 + age n인 여러 age 객체의 합이 Survivor 영역의 50%를 초과합니다. 이 때 age n(포함) 이상의 객체는 모두 Old Generation에 포함됩니다. 이 규칙은 실제로 오랫동안 살아남을 수 있는 개체가 가능한 한 빨리 노년기에 진입하기를 바라는 것입니다. 객체 동적 연령 판단 메커니즘은 일반적으로 마이너 GC 이후에 트리거됩니다.

구세대 공간 할당 보장 메커니즘

Young 세대의 각 Minor GC 이전에 JVM은 Old 세대의 남은 여유 공간을 계산합니다.

사용 가능한 공간이 Young Generation에 존재하는 모든 객체(가비지 객체 포함)의 크기를 합한 것보다 작은 경우

"-XX:-HandlePromotionFailure" 매개변수(jdk1.8에서 기본적으로 설정됨)가 설정되어 있는지 확인합니다.

이 매개변수가 있으면 이전 세대에서 사용 가능한 메모리 크기가 각 이전 마이너 GC 이후 이전 세대에 들어간 개체의 평균 크기보다 큰지 확인합니다.

이전 단계의 결과가 이전 단계보다 작거나 앞서 언급한 매개변수가 설정되지 않은 경우 Full gc가 트리거되고, 여전히 새 세대를 저장할 공간이 충분하지 않은 경우 Old Generation과 Young Generation이 함께 수집됩니다. 재활용 후 개체가 발생하면 "OOM"이 발생합니다.

물론, Minor GC 이후 Old Generation으로 옮겨야 할 남은 객체의 크기가 여전히 Old Generation의 사용 가능한 공간보다 큰 경우 Full GC 이후에도 Full GC가 트리거됩니다. 마이너 gc 이후에도 살아남은 개체를 위한 공간이 없으면 "OOM"도 발생합니다.

도달성 분석 알고리즘

JDK 소스 코드 레벨에서 JVM 클래스 로딩 메커니즘 분석

클래스 로딩 과정

클래스 로더 및 상위 위임 메커니즘

부모 위임을 설계하는 이유는 무엇입니까?

Tomcat 커스텀 로더에 대한 자세한 설명

Tomcat의 여러 주요 클래스 로더:

commonLoader: Tomcat의 가장 기본적인 클래스 로더입니다. 로딩 경로의 클래스는 Tomcat 컨테이너 자체와 각 웹앱에서 액세스할 수 있습니다.

catalinaLoader: Tomcat 컨테이너의 전용 클래스 로더입니다. 로딩 경로의 클래스는 Webapp에 표시되지 않습니다.

sharedLoader: 각 Webapp에서 공유하는 클래스 로더입니다. 로딩 경로의 클래스는 모든 Webapp에 표시되지만 Tomcat 컨테이너에는 표시되지 않습니다.

WebappClassLoader: 각 Webapp의 비공개 클래스 로더는 war 패키지의 관련 클래스 로드와 같이 현재 Webapp에만 표시됩니다. 각 war 패키지 애플리케이션에는 서로 다른 war 패키지와 같이 상호 격리를 달성하기 위한 자체 WebappClassLoader가 있습니다. 애플리케이션 구현 시 해당 스프링 버전을 로드할 수 있도록 다양한 스프링 버전이 도입되었습니다.

메모리 모델

가비지 수집 알고리즘

가비지 수집기

가비지 수집을 위한 기본 알고리즘 구현