大量DelegatingClassLoader类加载器,导致Perm区溢出
當使用Java反射時,Java虛擬機有兩種方法獲取被反射的類的信息。它可以使用一個JNI存取器;如果使用Java字節碼存取器,則需要擁有它自己的Java類和類加載器(sun/reflect/GeneratedMethodAccessor類和sun/reflect/DelegatingClassLoader),這些類和類加載器使用本機內存。字節碼存取器也可以被JIT編譯,這樣會增加本機內存的使用。如果Java反射被頻繁使用,會顯著地增加本機內存的使用。
Java虛擬機會首先使用JNI存取器,然后在訪問了同一個類若干次后,會改為使用Java字節碼存取器。這種當Java虛擬機從JNI存取器改為字節碼存取器的行為被稱為膨脹(Inflation)。Inflation機制提高了反射的性能,但是對于重度使用反射的項目可能存在隱患,它帶來了兩個問題:(1)初次加載的性能損失;(2)動態加載的字節碼導致PermGen持續增長。幸運的是,我們可以通過一個設置-Dsun.reflect.inflationThreshold=N控制這種行為,sun.reflect.inflationThreshold會告訴Java虛擬機使用JNI存取器多少次。如果設為0,則總是使用JNI存取器。
正常情況下,在Java中類是永久存在的。所以一旦類被加載,他們將一直停留在內存中,即便其所在應用服務器端已經停止運行。像cglib這樣的動態類產生庫,在他們動態創建了大量類之后,會使用大量永久代內存空間。在運行時創建的代理類被廣泛地使用,當單個類定義被用于產生多個實例時,創建新的代理類將會很容易。Spring和Hibernate經常產生某個類的代理,這些代理類被類加載器加載,產生的類定義從不被清理,導致永久性堆空間迅速填滿。
關掉Inflation會帶來一定程度上的性能損失,因此不到萬不得已的情況下并不要將其關閉,sun.reflect.inflationThreshold的默認值在不同的實現版本中有不同的值,例如在IBM Developer Kit for Java 5.0 的默認值為15。可以嘗試將這個值設置大一點,例如100。雖然Java中的class放在perm區中默認是不被GC的,但是我們可以指定讓他也參與GC,通過打開兩個參數:-XX+CMSClassUnloadingEnabled -XX+UseConcMarkSweepGC,這樣GC的時候在perm區中的垃圾class元數據也會被回收掉,從而釋放perm區的內存空間。JDK8已經沒有了perm區的概念,類的元數據被存放在Metaspace中,會自動進行垃圾回收,卸載掉不再使用的類。
另外需要注意的是,檢查JVM啟動參數中是否使用了-Xnoclassgc,如果增加了這個參數,class是不會被GC掉的,需要去掉這個參數。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的大量DelegatingClassLoader类加载器,导致Perm区溢出的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用反射代理类加载器的潜在内存使用问题
- 下一篇: 关于反射调用方法的一个log