1. 程式人生 > 實用技巧 >2020.8.26

2020.8.26

一、異常類關係圖

二、堆記憶體原理

原理

JVM堆記憶體分為2塊:PermanentSpace 和 Heap Space。
  • Permanent 即持久代(Permanent Generation),主要存放的是Java類定義資訊,與垃圾收集器要收集的Java物件關係不大。
  • Heap = { Old + (NEW = {Eden, from, to}) },Old 即 年老代(Old Generation),New 即年輕代(Young Generation)。年老代和年輕代的劃分對垃圾收集影響比較大。

年輕代

所有新生成的物件首先都是放在年輕代。年輕代的目標就是儘可能快速的收集掉那些生命週期短的物件。年輕代一般分3個區,1個Eden區,2個Survivor區(from 和 to)。

大部分物件在Eden區中生成。當Eden區滿時,還存活的物件將被複制到Survivor區(兩個中的一個),當一個Survivor區滿時,此區的存活物件將被複制到另外一個Survivor區,當另一個Survivor區也滿了的時候,從前一個Survivor區複製過來的並且此時還存活的物件,將可能被複制到年老代。

2個Survivor區是對稱的,沒有先後關係,所以同一個Survivor區中可能同時存在從Eden區複製過來物件,和從另一個Survivor區複製過來的物件;而複製到年老區的只有從另一個Survivor區過來的物件。而且,因為需要交換的原因,Survivor區至少有一個是空的。特殊的情況下,根據程式需要,Survivor區是可以配置為多個的(多於2個),這樣可以增加物件在年輕代中的存在時間,減少被放到年老代的可能。

針對年輕代的垃圾回收即 Young GC。

年老代

在年輕代中經歷了N次(可配置)垃圾回收後仍然存活的物件,就會被複制到年老代中。因此,可以認為年老代中存放的都是一些生命週期較長的物件。

針對年老代的垃圾回收即 Full GC。

持久代

用於存放靜態型別資料,如 Java Class, Method 等。持久代對垃圾回收沒有顯著影響。但是有些應用可能動態生成或呼叫一些Class,例如 Hibernate CGLib 等,在這種時候往往需要設定一個比較大的持久代空間來存放這些執行過程中動態增加的型別。

所以,當一組物件生成時,記憶體申請過程如下:

  1. JVM會試圖為相關Java物件在年輕代的Eden區中初始化一塊記憶體區域。
  2. 當Eden區空間足夠時,記憶體申請結束。否則執行下一步。
  3. JVM試圖釋放在Eden區中所有不活躍的物件(Young GC)。釋放後若Eden空間仍然不足以放入新物件,JVM則試圖將部分Eden區中活躍物件放入Survivor區。
  4. Survivor區被用來作為Eden區及年老代的中間交換區域。當年老代空間足夠時,Survivor區中存活了一定次數的物件會被移到年老代。
  5. 當年老代空間不夠時,JVM會在年老代進行完全的垃圾回收(Full GC)。
  6. Full GC後,若Survivor區及年老代仍然無法存放從Eden區複製過來的物件,則會導致JVM無法在Eden區為新生成的物件申請記憶體,即出現“Out of Memory”。

OOM(“Out of Memory”)異常一般主要有如下2種原因:

1. 年老代溢位,表現為:java.lang.OutOfMemoryError:Javaheapspace 這是最常見的情況,產生的原因可能是:設定的記憶體引數Xmx過小或程式的記憶體洩露及使用不當問題。 例如迴圈上萬次的字串處理、建立上千萬個物件、在一段程式碼內申請上百M甚至上G的記憶體。還有的時候雖然不會報記憶體溢位,卻會使系統不間斷的垃圾回收,也無法處理其它請求。這種情況下除了檢查程式、列印堆記憶體等方法排查,還可以藉助一些記憶體分析工具,比如MAT就很不錯。
2. 持久代溢位,表現為:java.lang.OutOfMemoryError:PermGenspace 通常由於持久代設定過小,動態載入了大量Java類而導致溢位,解決辦法唯有將引數 -XX:MaxPermSize 調大(一般256m能滿足絕大多數應用程式需求)。將部分Java類放到容器共享區(例如Tomcat share lib)去載入的辦法也是一個思路,但前提是容器裡部署了多個應用,且這些應用有大量的共享類庫。

引數說明

  • Xmx3550m:設定JVM最大堆記憶體為3550M。
  • -Xms3550m:設定JVM初始堆記憶體為3550M。此值可以設定與-Xmx相同,以避免每次垃圾回收完成後JVM重新分配記憶體。
  • -Xss128k:設定每個執行緒的棧大小。JDK5.0以後每個執行緒棧大小為1M,之前每個執行緒棧大小為256K。應當根據應用的執行緒所需記憶體大小進行調整。在相同實體記憶體下,減小這個值能生成更多的執行緒。但是作業系統對一個程序內的執行緒數還是有限制的,不能無限生成,經驗值在3000~5000左右。需要注意的是:當這個值被設定的較大(例如>2MB)時將會在很大程度上降低系統的效能。
  • -Xmn2g:設定年輕代大小為2G。在整個堆記憶體大小確定的情況下,增大年輕代將會減小年老代,反之亦然。此值關係到JVM垃圾回收,對系統性能影響較大,官方推薦配置為整個堆大小的3/8。
  • -XX:NewSize=1024m:設定年輕代初始值為1024M。
  • -XX:MaxNewSize=1024m:設定年輕代最大值為1024M。
  • -XX:PermSize=256m:設定持久代初始值為256M。
  • -XX:MaxPermSize=256m:設定持久代最大值為256M。
  • -XX:NewRatio=4:設定年輕代(包括1個Eden和2個Survivor區)與年老代的比值。表示年輕代比年老代為1:4。
  • -XX:SurvivorRatio=4:設定年輕代中Eden區與Survivor區的比值。表示2個Survivor區(JVM堆記憶體年輕代中預設有2個大小相等的Survivor區)與1個Eden區的比值為2:4,即1個Survivor區佔整個年輕代大小的1/6。
  • -XX:MaxTenuringThreshold=7:表示一個物件如果在Survivor區(救助空間)移動了7次還沒有被垃圾回收就進入年老代。如果設定為0的話,則年輕代物件不經過Survivor區,直接進入年老代,對於需要大量常駐記憶體的應用,這樣做可以提高效率。如果將此值設定為一個較大值,則年輕代物件會在Survivor區進行多次複製,這樣可以增加物件在年輕代存活時間,增加物件在年輕代被垃圾回收的概率,減少Full GC的頻率,這樣做可以在某種程度上提高服務穩定性。

三、

  final用於可以宣告屬性和方法,分別表示屬性的不可變及方法的不可繼承覆蓋

四、Listener

  HttpSessionAttributeListener:可以實現此偵聽器介面獲取此web應用程式中會話屬性列表更改的通知;

  HttpSessionBindingListener:當該物件從一個會話中被繫結或者解綁時通知該物件,這個物件由HttpSessionBindingEvent物件通知。這可能是servlet程式顯式地從會話中解繫結屬性的結果,可能是由於會話無效,也可能是由於會話超時;

  HttpSessionObjectListener:沒有該介面API;

  HttpSessionListener:當web應用程式中的活動會話列表發生更改時通知該介面的實現類,為了接收該通知事件,必須在web應用程式的部署描述符中配置實現類;

  HttpSessionActivationListener:繫結到會話的物件可以偵聽容器事件,通知它們會話將被鈍化,會話將被啟用。需要一個在虛擬機器之間遷移會話或持久會話的容器來通知所有繫結到實現該介面會話的屬性。