1. 程式人生 > >jvm 03-java堆記憶體模型

jvm 03-java堆記憶體模型

  • java中最大的特點在於其具備良好的垃圾收集特性
  • GC是整個java之中最重要的安全保證
  • 整個JVM中的GC的處理機制:對不需要的物件進行標記,而後進行清除

JVM堆記憶體劃分

  • 在JDK1.8之後,將最初的永久帶記憶體空間取消了,該圖為JDK1.8之前的記憶體空間組成
  • 取消永久代目的是為了將HotSpot於JRockit兩個虛擬機器標準聯合為一個
  • 在整個JVM堆記憶體之中實際上將記憶體分為了三部分:
    • 新生帶(年輕代):新物件和沒達到一定年齡的物件都在年輕代
    • 老年代:被長時間使用的物件,老年代的記憶體空間應該要比年輕代更大
    • 元空間(JDK1.8之前叫永久代):像一些方法中的操作臨時物件等,JDK1.8之前是佔用JVM記憶體,JDK1.8之後直接使用實體記憶體

GC流程

  • 基本所有資料都會儲存在JVM的堆記憶體之中
  • 對於整個的GC流程裡面,最需要處理的事年輕代與老年代的記憶體清理操作
  • 元空間(永久代)都不在GC範圍內

具體流程:

  1. 當現在有一個新的物件產生,JVM需要為該物件進行記憶體空間的申請
  2. 先判斷Eden區是否有記憶體空間,如果有,直接將新物件儲存在Eden區
  3. 如果Eden區的記憶體空間不足,會自動執行一個Minor GC操作,將Eden區的無用記憶體空間進行清理
  4. 清理Eden區之後繼續判斷Eden區記憶體空間情況,如果充足,則將新物件直接儲存在Eden區
  5. 如果執行了Minor GC之後發現Eden區的記憶體依然不足,那就判斷存活區的記憶體空間,並將Eden區的部分活躍物件儲存在存活區
  6. 活躍物件遷移到存活區後,繼續判斷Eden區記憶體空間情況,如果充足,則將新物件直接儲存在Eden區
  7. 如果存活區也沒有空間了,則繼續判斷老年區,如果老年區充足,則將存活區的部分活躍物件儲存在老年區
  8. 存活區的活躍物件遷移到老年區後,則將Eden區的部分活躍物件儲存在存活區
  9. 活躍物件遷移到存活區後,繼續判斷Eden區記憶體空間情況,如果充足,則將新物件直接儲存在Eden區
  10. 如果老年區也滿了,這時候產生Major GC(Full GC)進行老年區的記憶體清理
  11. 如果老年區執行了Major GC之後發現無法進行物件儲存,會產生OutOfMemoryError異常

堆記憶體引數調整(調優關鍵)

  • 實際上每一塊子記憶體區中都會存在有一部分的可變伸縮區
  • 如果空間不足時,則在可變範圍之內擴大記憶體空間
  • 當一段時間後,記憶體空間有餘,再將可變空間進行釋放

堆記憶體空間調整引數

  • -Xms:設定初始分配大小,預設為實體記憶體的1/64
  • -Xmx:最大分配記憶體,預設為實體記憶體的1/4
  • -XX:+PrintGCDetails:輸出詳細的GC處理日誌
  • -XX:+PrintGCTimeStamps:輸出GC的時間戳資訊
  • -XX:+PrintGCDateStamps:輸出GC的時間戳資訊(以日期的形式)
  • -XX:+PrintHeapAtGC:在GC進行處理的前後列印堆記憶體資訊
  • -Xloggc:(SavePath):設定日誌資訊儲存檔案
  • 在堆記憶體的調整策略中,基本上只要調整兩個引數:-Xms和-Xmx

可通過Runtime類獲取記憶體的整體資訊

程式碼如下:

package cn.liang.jvm;
public class memoryTest {
  public static void main(String[] args) {
      Runtime runtime = Runtime.getRuntime();
      long maxMemory = runtime.maxMemory();
      long totalMemory = runtime.totalMemory();
      System.out.println("max_memory=" + maxMemory /(double)1024/1024 + "M");
      System.out.println("total_memory=" + totalMemory /(double)1024/1024 + "M");
  }
}

輸出結果:

max_memory=3641.0M
total_memory=245.5M

說明整個記憶體空間的可變範圍(伸縮區):245.5M ~ 3641.0M之間,有可能造成整個程式的效能


為了避免伸縮區的可調策略,使初始化記憶體等於最大記憶體,從而提升整個程式效能


輸出結果:

max_memory=981.5M
total_memory=981.5M
Heap
 PSYoungGen      total 305664K, used 15729K [0x00000007aab00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 262144K, 6% used [0x00000007aab00000,0x00000007aba5c420,0x00000007bab00000)
  from space 43520K, 0% used [0x00000007bd580000,0x00000007bd580000,0x00000007c0000000)
  to   space 43520K, 0% used [0x00000007bab00000,0x00000007bab00000,0x00000007bd580000)
 ParOldGen       total 699392K, used 0K [0x0000000780000000, 0x00000007aab00000, 0x00000007aab00000)
  object space 699392K, 0% used [0x0000000780000000,0x0000000780000000,0x00000007aab00000)
 Metaspace       used 2708K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 293K, capacity 386K, committed 512K, reserved 1048576K

觀察GC的觸發操作

程式碼如下:

package cn.liang.jvm;
import java.util.Random;
public class gctest {
  public static void main(String[] args) {
      Random random = new Random();
      String str = "hello liang";
      while (true) {
          str +=str + random.nextInt(99999999);
          str.intern();   
      }           
  }
}

輸出結果:

[GC (Allocation Failure) [PSYoungGen: 1769K->511K(2560K)] 1769K->775K(9728K), 0.0015982 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2374K->240K(2560K)] 2638K->1119K(9728K), 0.0011725 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2100K->256K(2560K)] 7841K->5996K(9728K), 0.0005402 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 256K->240K(2560K)] 5996K->5980K(9728K), 0.0005811 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 240K->0K(2560K)] [ParOldGen: 5740K->3925K(7168K)] 5980K->3925K(9728K), [Metaspace: 2662K->2662K(1056768K)], 0.0064126 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 41K->32K(2560K)] 6397K->6388K(9728K), 0.0003653 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 32K->32K(1536K)] 6388K->6388K(8704K), 0.0003294 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 32K->0K(1536K)] [ParOldGen: 6356K->2710K(7168K)] 6388K->2710K(8704K), [Metaspace: 2662K->2662K(1056768K)], 0.0035285 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 19K->0K(2048K)] 5160K->5140K(9216K), 0.0004489 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 5140K->5140K(9216K), 0.0003114 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 5140K->5140K(7168K)] 5140K->5140K(9216K), [Metaspace: 2662K->2662K(1056768K)], 0.0030502 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 5140K->5140K(9216K), 0.0003198 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 5140K->5127K(7168K)] 5140K->5127K(9216K), [Metaspace: 2662K->2662K(1056768K)], 0.0039555 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  at java.util.Arrays.copyOf(Arrays.java:3332)
  at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
  at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
  at java.lang.StringBuilder.append(StringBuilder.java:136)
  at cn.liang.jvm.gctest.main(gctest.java:11)
Heap
 PSYoungGen      total 2048K, used 40K [0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 1024K, 3% used [0x00000007bfd00000,0x00000007bfd0a120,0x00000007bfe00000)
  from space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
  to   space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
 ParOldGen       total 7168K, used 5127K [0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000)
  object space 7168K, 71% used [0x00000007bf600000,0x00000007bfb01c78,0x00000007bfd00000)
 Metaspace       used 2693K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 292K, capacity 386K, committed 512K, reserved 1048576K

日後如果發現你的程式執行速度變慢了,可以針對程式的執行記憶體進行分析

  • 視覺化工具:..\Java\jdk1.8.0_131\bin\jvisualvm.exe
  • 命令檢視:jmap(jmap -heap JavaPID)