解密Java內存溢出之持久代


垃圾回收是Java程序員了解最少的一部分。他們認為Java虛擬機接管了垃圾回收,因此沒必要去擔心內存的申請,分配等問題。但是隨着應用越來越復雜,垃圾回收也越來越復雜,一旦垃圾回收變的復雜,應用的性能將會大打折扣。所以,Java程序員了解垃圾回收的機制並且知道怎樣解決“內存溢出”問題會有很大的益處。在Java中,有兩個非常普遍的內存溢出問題。一個是堆內存溢出,另一個是持久代內存溢出。

持久代和類加載器

       Java對象是java 類的實例。每當創建一個Java對象時,Java虛擬機都會創建該對象的內部引用並且保存在堆中。如果一個類是第一次訪問,那么它必須通過Java虛擬機加載進來。類加載是定位,尋找,加載class文件和解析class文件結構的過程。類加載器負責確保加載正確的class文件。Java程序里面每一個class文件需要被同一個類加載加載。類加載器是 java.lang.ClassLoader 類的實例。目前為止,類加載器是在持久代空間里面加載類的。

       Java虛擬機也創建了Java類的內部引用保存在持久代。在垃圾回收期間,Java對象和類都當做對象處理並且以同樣的方式回收。最初Java對象和類都是保存在堆中。

一個性能優化措施:一旦持久代創建后,就會把classes放入里面。Classes是Java虛擬機的部分實現,我們不應該用我們的數據結構填滿Java堆。持久代包含以下類信息:

  • 類方法
  • 類名稱
  • 常量池信息
  • 對象數組和與類相關的類型數組
  • 被Java虛擬機使用的內部對象
  • 編譯器用於優化的信息

現在我們知道了持久代是什么,接下來看看在這塊會是什么原因引起內存問題。

持久代空間

       當Java虛擬機需要加載定義的一個新class,但是在持久代中沒有足夠的空間就會拋出‘Java.Lang.OutOfMemoryError: PermGen Space’異常。默認分配給持久代的大小在server模式下是64MB ,在client模式下是32MB 。這就有兩個原因可能會引起持久代內存溢出問題的發生。

第一個原因可能是你應用或者服務器已經有非常多的class在你的持久代中,已經不能容納所有的class了。

-XX:MaxPermSize=XXXM

        如果是因為大量的class導致持久代的空間的不足引起的問題,那么你可以增加持久代的大小通過–XX:MaxPermSize=XXm  參數。這將增加持久代的可用空間來保存class。看起來像這樣: -XX:MaxPermSize=256m

-XX:+CMSClassUnloadingEnabled

       這個參數表示在使用CMS垃圾回收機制的時候是否啟用類卸載功能。默認這個是設置為不啟用的,所以你想啟用這個功能你需要在Java參數中明確的設置下面的參數:

-XX:+CMSClassUnloadingEnabled

如果你啟用了CMSClassUnloadingEnabled ,垃圾回收會清理持久代,移除不再使用的classes。這個參數只有在 UseConcMarkSweepGC  也啟用的情況下才有用。參數如下:

-XX:+UseConcMarkSweepGC

-XX:+CMSPermGenSweepingEnabled

       這個參數表示是否會清理持久代。默認是不清理的,因此我們需要明確設置這個參數來調試持久代內存溢出問題。這個參數在Java6中被移除了,因此你需要使用 -XX:+CMSClassUnloadingEnabled 如果你是使用Java6或者后面更高的版本。那么解決持久代內存大小問題的參數看起來會是下面這樣子:

-XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC XX:+CMSClassUnloadingEnabled

內存泄露

     第二個原因可能是內存泄露。那么加載的類怎樣變成不用的呢?

     在Java中通常class是永久存在的。一旦class被加載,他們就呆在內存中,即使服務器上的應用停掉了。像cglib這樣可以動態產生class的類庫會使用很多持久代空間,因為它動態的創建很多class。頻繁的使用在運行時創建的代理類。當一個類定義可以為多個實例重用時很容易創建新的代理類。

       Sping和Hibernate經常會代理某些class。這些代理的class是通過類加載器加載的。產生的類定義如果一直不回收就會導致持久代空間很快就滿了。

       你需要確定是不是內存泄露引起的持久代空間的問題,同時解決它。增加持久代空間大小將不會有用,這只會延遲它的發生,因為在某個時間點持久代還是會被填滿。

       如果你正在使用tomcat遇到了內存泄露問題,最新版本的tomcat有能力處理一些內存泄露問題。詳細請看:

 總結

       一旦你遇到了持久代內容溢出問題,你需要找出這個問題是因為加載了大量的class文件還是內存泄露引起的。如果是因為class的數量過多,你可以增加持久代分配的空間大小來解決這個問題。如果是因為內存泄露,你需要引起內存泄露的根源所在並且定位它。一些框架像cglib,Spring,Hibernate會創建大量的動態類,因此對於使用這些框架的應用最好是分配多一點的持久代空間。

原文鏈接: http://www.javacodegeeks.com/2013/12/decoding-java-lang-outofmemoryerror-permgen-space.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM