1. 程式人生 > >java記憶體溢位之持久代

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會建立大量的動態類,因此對於使用這些框架的應用最好是分配多一點的持久代空間。