1. 程式人生 > >Java虛擬機器,GC垃圾回收機制

Java虛擬機器,GC垃圾回收機制

一,Java記憶體區域

1,用一張圖來表示記憶體區域

這裡寫圖片描述

2,執行緒隔離的資料區(執行緒私有記憶體區域,執行緒之間互不影響)

①,程式計數器:是一塊較小的記憶體空間,可以看成是當前執行緒所執行的位元組碼的行號指示器。如果執行緒正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的地址;如果正在執行的是Native方法,這個計數器值為(Undefined)。此記憶體區域是唯一一個在Java虛擬機器規範中沒有規定任何OutOfMemoryError情況的區域。
②,Java虛擬機器棧:它的生命週期與執行緒相同。每個方法在執行時會建立一個棧幀,用於儲存區域性變量表,運算元棧,動態連結

等。每個方法從執行到完成的過程,就對應著一個棧幀在虛擬機器棧中的入棧到出棧的過程。Java虛擬機器棧規定了兩個異常:StackOverflowError異常和OutOfMemoryError異常。
③,本地方法棧:它和Java虛擬機器棧的區別,虛擬機器棧為虛擬機器提供Java方法服務,本地方法棧為虛擬機器提供Native方法服務。本地方法棧規定了兩個異常:StackOverflowError異常和OutOfMemoryError異常。

注意:當執行緒請求的記憶體大小大於所配置的初始化大小,將丟擲StackOverflowError。比如配置大小為128M,而棧裡的記憶體分配超過了這個大小,通常發生在方法遞迴呼叫深度過大時候;如果JVM記憶體大小是可擴充套件的,當然一般都是可以擴充套件的,當自動擴充套件到計算機本身記憶體大小時會丟擲OutOfMemoryError。比如記憶體為4G,當JVM超過配置大小自動擴充套件至4G時會丟擲OutOfMemoryError。

3,共享資料區
①,Java堆:是Java虛擬機器管理的記憶體中最大的一塊,在虛擬機器啟動時建立。此記憶體區域的唯一目的就是存放物件例項,是垃圾收集器管理的主要區域。如果在堆中沒有記憶體完成例項分配,並且堆也無法再擴充套件時,會丟擲OutOfMemoryError。
②,方法區:此記憶體區域用於儲存已被虛擬機器載入的類資訊,常量,靜態變數等資料。

4,執行時常量池
執行時常量池是方法區的一部分,用於存放編譯期生成的各種字面量和符號引用。

5,直接記憶體
直接記憶體不是虛擬機器執行時資料區的一部分,Buffer緩衝區就是直接記憶體,它可以使用Native函式庫直接分配堆外記憶體。雖然直接記憶體不會受到Java堆大小的限制,但是,會收到本機總記憶體大小以及處理器定址空間的限制。

二,垃圾收集器與記憶體分配策略

1,簡單介紹垃圾收集器
Serial收集器,ParNew收集器,Parallel Scavenge收集器,Serial Old收集器,Parallel Old收集器,CMS收集器,G1收集器
2,記憶體分配
①,Java 程式執行時的記憶體分配策略有三種,分別是靜態分配,棧式分配,和堆式分配,對應的,三種儲存策略使用的記憶體空間主要分別是靜態儲存區(也稱方法區)、棧區和堆區。
②,靜態儲存區(方法區):主要存放靜態資料、全域性 static 資料和常量。這塊記憶體在程式編譯時就已經分配好,並且在程式整個執行期間都存在。
③,棧區 :當方法被執行時,方法體內的區域性變數(其中包括基礎資料型別、物件的引用)都在棧上建立,並在方法執行結束時這些區域性變數所持有的記憶體將會自動被釋放。因為棧記憶體分配運算內置於處理器的指令集中,效率很高,但是分配的記憶體容量有限。
④,堆區 : 又稱動態記憶體分配,通常就是指在程式執行時直接 new 出來的記憶體,也就是物件的例項。這部分記憶體在不使用時將會由 Java 垃圾回收器來負責回收。

三,虛擬機器效能監控與故障處理工具

JDK安裝目錄bin中有很多工具,這裡就介紹幾個

1,JDK監控和故障處理工具(命令列工具)
①,jps:JVM Process Status Tool,顯示指定系統內所有的HotSpot虛擬機器程序
②,jstat:JVM Statistics monitoring Tool,用於手機HotSpot虛擬機器各方面的執行資料
③,jinfo:Configuration Info for Java,顯示虛擬機器配置資訊
④,jmap:Memory Map for Java,生成虛擬機器的記憶體轉儲快照(heapdump檔案)
⑤,jhat:JVM Heap Dump Browser,用於分析heapdump檔案,它會建立一個HTTP/HTML伺服器,讓使用者可以在瀏覽器上檢視分析結果
⑥,jstack:Stack Trace for Java,顯示虛擬機器的執行緒快照

2,JConsole:Java監視與管理控制檯
Jconsole是一種基於JMX的視覺化監視,管理工具。通過JDK/bin中的“jconsole.exe”可以啟動,啟動之後可以雙擊一個程序,也可以連線遠端程序,對遠端虛擬機器進行監控

這裡寫圖片描述

Jconsole主頁面,可以看到概覽,記憶體,執行緒,類等資訊

這裡寫圖片描述

四,記憶體模型

Java記憶體模型的主要目標是定義程式中各個變數的訪問規則,即在虛擬機器中將變數儲存到記憶體和從記憶體中取出變數這樣底層細節。此處的變數與Java程式設計時所說的變數不一樣,指包括了例項欄位、靜態欄位和構成陣列物件的元素,但是不包括區域性變數與方法引數,後者是執行緒私有的,不會被共享。

1,主記憶體與工作記憶體
這裡講的記憶體模型和上邊講的Java記憶體區域沒有關係,這裡記憶體模型偏向於硬體。Java記憶體模型中規定了所有的變數都儲存在主記憶體中,每條執行緒還有自己的工作記憶體(可以與前面將的處理器的快取記憶體類比),執行緒的工作記憶體中儲存了該執行緒使用到的變數到主記憶體副本拷貝,執行緒對變數的所有操作(讀取、賦值)都必須在工作記憶體中進行,而不能直接讀寫主記憶體中的變數。不同執行緒之間無法直接訪問對方工作記憶體中的變數,執行緒間變數值的傳遞均需要在主記憶體來完成,執行緒、主記憶體和工作記憶體的互動關係如下圖所示

這裡寫圖片描述

2,記憶體間互動操作

①,記憶體間互動即一個變數如何從主記憶體拷貝到工作記憶體,從工作記憶體同步回主記憶體。Java記憶體模型定義了8中變數

  • lock(鎖定):作用於主記憶體的變數,它把一個變數標識為一條執行緒獨佔的狀態。
  • unlock(解鎖):作用於主記憶體的變數,它把一個處於鎖定狀態的變數釋放出來,釋放後的變數才可以被其他執行緒鎖定。
  • read(讀取):作用於主記憶體的變數,它把一個變數的值從主記憶體傳輸到執行緒的工作記憶體中,以便隨後的load動作使用。
  • load(載入):作用於工作記憶體的變數,它把read操作從主記憶體中得到的變數值放入工作記憶體的變數副本中。
  • use(使用):作用於工作記憶體的變數,它把工作記憶體中的一個變數的值傳遞給執行引擎,每當虛擬機器遇到一個需要使用到變數的位元組碼指令時將會執行這個操作。
  • assign(賦值):作用於工作記憶體的變數,它把一個從執行引擎接收到的賦值給工作記憶體的變數,每當虛擬機器遇到一個給變數賦值的位元組指令時執行這個操作。
  • store(儲存):作用於工作記憶體的變數,它把工作記憶體中的一個變數的值傳送到主記憶體中,以便隨後的write的操作使用。
  • write(寫入):作用於工作記憶體的變數,它把store操作從工作記憶體中得到的變數的值放入住記憶體的變數中。

②,這些變數是如何執行的呢?如果要把一個變數從主記憶體複製到工作記憶體,那就要順序執行read和load操作,如果要把變數從工作記憶體同步回主記憶體,就要順序執行store和write操作。注意,Java記憶體模型只要求上述兩個操作必須按順序執行。

③,Java記憶體模型還規定了在執行上述8種操作時必須滿足如下規則:

  • 不允許read和load,store和write操作之一單獨出現,即不允許一個變數從主記憶體讀取了但工作記憶體不接受,或者從工作記憶體發起回寫了但主記憶體不接收的情況出現。
  • 不允許一個執行緒丟棄它的最近的assign操作,即變數在工作記憶體中改變了之後必須該變化同步回主記憶體。
  • 不允許一個執行緒無原因地(沒有發生過任何assign操作)把資料從執行緒的工作記憶體同步回主記憶體中。
  • 一個新的變數只能在主記憶體中“誕生”,不允許在工作記憶體中直接使用一個未被初始化(load或assign)的變數,換句話,就是對一個變數實施use,store操作之前,必須先執行過了assign和load操作。
  • 一個變數在同一個時刻只允許一條執行緒對其進行lock操作,但lock操作可以被同一條執行緒重複執行多次,多次執行lock後,只有執行相同次數的unlock操作,變數才會被解鎖。
  • 如果對一個變數執行lock操作,那將會清空工作記憶體中此變數的值,在執行引擎使用這個變數前,需要重新執行load或assign操作初始化變數的值。
  • 如果一個變數事先沒有被lock操作鎖定,那就不允許對它執行unlock操作,也不允許去unlock一個被其它執行緒鎖定住的變數。
  • 對一個變數執行unlock操作之前,必須先把此變數同步回主記憶體(執行store,write操作)。