1. 程式人生 > >JVM、Gc工作機制詳解

JVM、Gc工作機制詳解

相同 生命 棧幀 VM 每次 失效 劃分 地址 .com

JVM主要包括四個部分:

  1. 類加載器(ClassLoad)
  2. 執行引擎
  3. 內存區:
  4. 本地方法接口:類似於jni調本地native方法

內存區包括四個部分:

1.方法區:包含了靜態變量、常量池、構造函數等

2.Java堆:java實例或者是對象

3.Java棧:java棧總是和線程關聯在一起,每當創建一個線程時,JVM就會為這個線程創建一個對應的java棧。在這個java棧中又會包含多個棧幀,每運行一個方法就創建一個棧幀,用於存儲局部變量表、操作棧、方法返回值等。每 一個方法從調用直至執行完成的過程,就對應一個棧幀在java棧中入棧到出棧的過程。所以java棧是現成私有的。

4.程序計數器:保存線程執行的內存地址

5.本地方法棧(Native Method Stack):和java棧的作用差不多,只不過是為JVM使用到的native方法服務的。

內存分配

  內存分配主要有3種方法:靜態分配、棧式分配、堆式分配

  靜態分配:主要是靜態存儲區,存放靜態變量、全局static數據和常量,在程序編譯的時候就被分配,在整個程序運行的過程中都存在。

  棧式分配:一般存放方法中的局部變量(基礎數據類型和局部對象),方法執行結束後變量所持有的內存都會被釋放,棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。

  堆式分配:又稱動態分配,一般是new出來的對象,會被gc回收。

垃圾檢測、回收算法

垃圾檢測的方法:

  1.引用計數法:給一個對象添加引用計數器,每當有個地方引用它,計數器就加1;引用失效就減1,當計數器為0的時候就會被回收,但是這個方法有一個缺陷是如果兩個對象互相引用,但是沒有被其他的對象引用,那這兩個對象肯定是垃圾對象,但是此刻的計數器不會為0,不會被回收。

  2.可達性分析法:

技術分享圖片

  這個算法的基本思路就是通過一系列的稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連(用圖論的話來說,就是從GC Roots到這個對象不可達)時,則證明此對象是不可用的。如圖3-1所示,對象object 5、object 6、object 7雖然互相有關聯,但是它們到GC Roots是不可達的,所以它們將會被判定為是可回收的對象。

  在Java語言中,可作為GC Roots的對象包括下面幾種:

    1.虛擬機棧(棧幀中的本地變量表)中引用的對象。

    2.方法區中類靜態屬性引用的對象。

    3.方法區中常量引用的對象。

    4.本地方法棧中JNI(即一般說的Native方法)引用的對象。

垃圾回收的算法:

  1.標記清除:這種算法分為兩個階段,第一步是從根節點標記所有被引用的對象,第二步是從這個堆中清除其他未被引用的對象,這種算法有一個弊端是清除後的內存是不連續的,產生了很多的碎片。

技術分享圖片

  2.復制:將內存劃分為兩個相等大小的區域,每次只使用其中的一個區域,將正在使用的對象復制到另外一片內存中,還可以進行相應的整理,這種算法的缺點是需要兩倍的內存。

技術分享圖片

  3.標記-整理:復制算法的高效性是建立在存活對象少、垃圾對象多的前提下的。這種情況在新生代經常發生,但是在老年代更常見的情況是大部分對象都是存活對象。如果依然使用復制算法,由於存活的對象較多,復制的成本也將很高。

  標記-壓縮算法是一種老年代的回收算法,它在標記-清除算法的基礎上做了一些優化。首先也需要從根節點開始對所有可達對象做一次標記,但之後,它並不簡單地清理未標記的對象,而是將所有的存活對象壓縮到內存的一端。之後,清理邊界外所有的空間。這種方法既避免了碎片的產生,又不需要兩塊相同的內存空間,因此,其性價比比較高。

技術分享圖片

  4.分代收集算法:將對象按其生命周期的不同劃分成:年輕代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation)。其中持久代主要存放的是類信息,所以與java對象的回收關系不大,與回收息息相關的是年輕代和年老代。

JVM、Gc工作機制詳解