JVM 성능 문제를 찾을 때 메모리 누출이 발생하여 JVM Offemory가 발생할 수 있습니다. Tomcat 컨테이너를 사용할 때 매개 변수 Reloadable = "True"가 설정된 경우 응용 프로그램을 자주 배포 할 때 메모리 오버 플로우가 발생할 수도 있습니다. Tomcat의 뜨거운 배포 원리는 Web-Inf/Classes 또는 Web-Inf/Lib 디렉토리의 파일이 변경되고 응용 프로그램이 먼저 중지 된 다음 시작되는지 감지하는 것입니다. Tomcat은 기본적으로 각 응용 프로그램에 WebAppClassLoader를 할당하므로 핫 교체 원칙은 클래스를로드하기 위해 새 클래스 로더를 만드는 것입니다. JVM에서 클래스의 고유성은 클래스 파일과 클래스 로더에 의해 결정되므로 클래스를 다시로드하면 핫 교체의 목적을 달성 할 수 있습니다. 핫 배포 횟수가 더 자주 발생하면 JVM이 더 많은 클래스를로드합니다. 이전 클래스가 어떤 이유로 제 시간에 언로드되지 않으면 (예 : 메모리 누출) 영구 생성 또는 Metaspace Outfemory로 이어질 수 있습니다. 이 기사는 Threadlocal 및 Classloader가 메모리 누출과 궁극적으로 데모를 통해 제외를 유발하는 시나리오를 간략하게 소개합니다.
수업을 제거합니다
수업이 사용 된 후 다음 상황이 만족되면 제거됩니다.
1. 힙 의이 클래스의 모든 사례는 재활용되었습니다. 즉,이 클래스의 인스턴스 객체는 힙에 존재하지 않습니다.
2.이 클래스를로드하는 클래스 로더가 재활용되었습니다.
3.이 클래스에 해당하는 클래스 객체는 어디서나 참조 할 수 없으며 반사를 통해 클래스 객체에 액세스 할 수 없습니다.
클래스가 제거 조건을 충족하는 경우 JVM은 GC에있을 때 클래스를 제거합니다.
장면 소개
이전 기사에서는 ThreadLocal의 원리를 소개했습니다. 각 스레드에는 ThreadLocalMap이 있습니다. 스레드의 수명주기가 비교적 길면 ThreadLocalMap의 항목은 재활용되지 않을 수 있습니다. Threadlocal 객체는 항상 스레드에 의해 강력하게 참조되었습니다. 인스턴스 객체는 클래스 객체의 참조를 보유하므로 클래스 객체는로드하는 클래스 로더의 참조를 고정하여 클래스가 언로드됩니다. 충분한 클래스가로드되면 영구 생성 또는 Metaspace 메모리 오버플로가 발생할 수 있습니다. 클래스에 더 큰 바이트 어레이와 같은 큰 객체가있는 경우 Java 힙 영역의 메모리 오버플로가 발생합니다.
소스 코드 소개
여기 내부 클래스 내부가 있습니다. 내부 클래스에는 정적 ThreadLocal 객체가 있으며, 주로 스레드가 내부 클래스에 대한 강력한 참조를 유지하여 내부 클래스를 재활용 할 수 없도록하는 데 사용됩니다. 사용자 정의 클래스 로더는 다음과 같이 내부 클래스를로드하도록 정의됩니다.
public class memoryleak {public static void main (string [] args) {// 스레드가 항상 실행되고 있기 때문에 스레드 객체의 내부 객체는 스레드 객체 새 스레드 (new Runnable () {@override public void run () {while (true) {// 새로운 클래스로드 인 인스턴스가 만들어 지도록 만들어집니다. ( "load1", memoryLeak.class.getClassLoader (), "com.ezlippi.memoryLeak $ 내면", ComeNnerclass = Classloader.Modlippi.MemoryLeak 참조 처리 내부 클래스 로더 = thread. } // 힙 영역에 도달하기 위해 더 빠른 공공 정적 클래스 내부 {private byte [] mb = new Byte [1024 * 1024]; 정적 ThreadLocal <inner> threadLocal = new ThreadLocal <inner> () {@override protected Inner InitialValue () {return new Inner (); }}; // 내부 객체 static {threadlocal.get (); } public inner () {}} // 소스 코드를 생략하여 개인 정적 클래스 CustomClassLoader 확장 클래스 로더 {}힙 영역 메모리 오버 플로우
힙 메모리 오버 플로우를 트리거하려면 내부 클래스에 1MB 바이트 어레이를 설정하고 동시에 정적 블록에서 ThreadLocal.get ()를 호출해야합니다. 호출 만 Interner 객체를 초기화하기 위해 InitialValue () 만 트리거됩니다. 그렇지 않으면 빈 ThreadLocal 객체 만 생성하고 ThreadLocalMap에 데이터가 없습니다.
JVM 매개 변수는 다음과 같습니다.
-xms100m -xmx100m -xx :+useparnewgc -xx :+useconcmarksweepgc -xx :+printgcdetails -xx :+printheapatgc -xx :+printclasshistogram -xx :+heapdumponoutofmemoryerrror
지난 814 개의 실행 후 JVM 힙 영역 메모리는 다음과 같이 오버플로됩니다.
java.lang.outofMemoryError : java 힙 스페이스 힙에 java_pid11824.hprof ... 힙 덤프 파일 생성 된 힙 덤프 파일 [1.501 초의 100661202 바이트] 힙 파트 총 30720k, 중고 30389K [0x000000000f930000, 0x00000000FBD5000000FBD 5500000000000FBD 50000000 99% 사용 된 [0x00000000F9C000000, 0x00000000FB6AD450, 0x00000000FB6B0000) SPACE 3392K, 90% 사용 [0x0000000000FB6B0000, 0x000000FB9B0030, 0x00000000FBA00000)에서 0x00000000000000FBA00000) [0x0000000000FBA00000, 0x00000000FBD500000) 동시 마크 스위프 생성 총 68288K, 사용 67600K [0x000000000FBD500000, 0x0000001000000000, 0x000000010000000) METASPACE 사용 3770K, PROGITION 5134K, CAMINTED 5248K. 공간 474K, 용량 578K, 커밋 된 640K, 예약 된 1048576kexception in Thread "Java.lang.outofMemoryError : com.ezlippi.MemoryLeak $ inner. (memoryLeak.java:34)의 Java Heep Space에서 예약 된 1048576Keception. sun.reflect.newinstance0 (기본 메소드)에서 sun.reflect.newinstance (unknown source)에서 sun.reflect.delegatingconstructoraccessorimpl.newinstance (java.reffect.langfector)의 newinstance (unkensecessoraccessorimpl.newinstance)에서 sun.reflect.newinstance. 소스) java.lang.reflect.constructor.newinstance (알 수없는 출처) 소스) java.lang.class.newinstance (Unknown Source) (com.ezlippi.moryLeak $ 1.Run (memoryLeak.java:20)의 java.lang.run (알려지지 않은 출처)
힙 영역은 많은 1MB 바이트 어레이를 저장하기 때문에 JVM에 새로운 내부 물체를 만들 메모리가 없음을 알 수 있습니다. 여기서 나는 클래스의 히스토그램을 인쇄했습니다 (다음 그림은 힙 크기가 1024m 인 장면입니다). 바이트 배열은 855m의 공간을 차지하고 814 개의 com.ezlippi.memoryLeak $ CustomClassLoader 인스턴스가 생성되며 기본적으로 바이트 배열의 크기와 일치합니다.
num #instances #Bytes 클래스 이름------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- com.ezlippi.memoryLeak $ CustomClassLoader 12 : 820 53088 [ljava.util.hashtable $ entry; 15 : 817 39216 Java.util.hashtable 16 : 915 36600 Java.lang.ref.softreference 17 : 543 34752 Java.net.url 18 : 697 33456 Java.Nio.HeapCharbuffer 19 : 817 32680 Java.security. java.util.treemap $ 21 : 928 29696 Java.util.hashtable $ 항목 22 : 1802 28832 java.util.hashset 23 : 817 26144 java.security.codesource 24 : 814 26048 java.lang.
Metaspace 오버플로
Metaspace 오버 플로우를 만들려면 Metaspace의 공간을 약간 줄이고 힙 오버플로 전에 충분한 클래스를로드해야합니다. 따라서 JVM 매개 변수를 조정하고 바이트 배열의 크기를 1KB로 조정했습니다.
Private Byte [] KB = New Byte [1024]; -XMS100M -XMX100M -XX :+USECONCMARKSWEEPGC -XX :+PRINTGCDETAILS -XX :+PrintheApatgc -XX :+printClassHistOgram -XX = 2M -XX : 2M -XX : 2M -XX :
GC 로그에서 Meraspace가 GC 임계 값에 도달하면 (즉, MaxMetaspacesize 구성의 크기) FullGC가 트리거됩니다.
java.lang.outofMemoryError : metaspace << 스택 추적 없음 >> {heap wefore gc invocations = 20 (Full 20) : PAR New Generation Total 30720K, 사용 0K [0x0000000000F9C000000) EDEN SPACE 27328K, 0X000000000000, 0X00000000, 0x00000000F9C00000, 0x0000000F9C00000, 0x0000000F9C00000, 0x0000000FB6B0000)에서 3392K, 0% 사용 [0x00000000FB6B0000, 0x00000000FB6B0000, 0x00000000FBA00000)에서 3392K, 0x000000000000FBA00000) [0x000000000000FBA00000, 0x000000000FBD500000) 우주 3392K, 0% 사용 사용 [0x0000000000FBA00000, 0x000000010000000000) Metaspace 사용 1806K, 용량 198K, 2048K, 예약 된 1056768K Class Space, Committed 384K, Committed 384K, Committed 384K, Committed 384K. 1048576k [Full GC (메타 데이터 GC 임계 값) [CMSProcess가 출구 코드로 완료되었습니다 1위의 예에서 클래스 로더와 스레드 로컬이 부적절하게 사용되면 실제로 메모리 누출로 이어질 것임을 알 수 있습니다. 전체 소스 코드는 Github에 있습니다