JVM 垃圾回收

由於java時運行在虛擬機上的,因此與C、C++等語言不同,java可以自己進行內存垃圾的回收,不需要程序員自己處理對象的銷燬。

但是掌握GC對於我們對jvm調優有很大的幫助。

對於jvm的內存模型我們可以看到:


按照線程共享的方式可以分爲:

  • 線程共享:堆,方法區
  • 非線程共享:程序計數器,JVM棧,本地方法棧

解釋下各部分的含義:

  1. 方法區:主要存儲JVM加載的類信息,靜態變量,常量,即時編譯器編譯的代碼(這裏涉及到即時編譯器JIT相關內容,以後再做介紹)
  2. 堆:主要存儲生成的對象信息,即對象的實例。
  3. 程序計數器:程序執行的順序,當前執行的字節碼的行號。當java文件被編譯成class以後會形成一組以8位字節爲基礎單位的二進制流。程序運行到哪一步就是根據改變計數器 來實現的。最明顯的使用就是,當採用多線程的時候,單一處理器只能在一個時間點運行一個線程,而當多個線程搶佔資源或者共享資源的時候,需要記錄每個線程的程序執行到什麼位置,因此程序計數器是線程級的,各線程之間的計數器互不影響。
  4. JVM棧:主要存儲局部變量,操作數棧,動態鏈接,方法出口信息等。其中比較重要的就是局部變量表這塊,其中不光包括一些基本的數據類型,如int,long等 還包括一些對象的引用,如Integer等對象的地址。
  5. 本地方法棧:爲虛擬機使用到的native方法,使用native修飾的java方法只有聲明,他的實現需要用到其他語言。native關鍵字說明其修飾的方法是一個原生態方法,方法對應的實現不是在當前文件,而是在用其他語言(如C和C++)實現的文件中

比如現在有一個類

package dto;

import java.io.Serializable;

public class StudentDto implements Serializable {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 3676795379790467770L;
	
	private int id;
	private String name;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}

當通過StudentDto studentDto = newStudentDto();創建對象的時候,內存會爲做如下的操作


JVM棧中主要存儲的是對象的引用,堆中存放的是對象的數據。因此在內存回收的時候,主要針對的是堆內存的回收。

根據分代原理將內存劃分爲年輕代,年老代和持久代,其中持久代主要存儲的是編譯的類,方法,常量池,字段和方法數據,構造函數和普通方法的字節碼內容以及類、實例、接口初始化時需要使用到的特殊方法等數據。

具體分代如下圖


根據複製算法:

其中一個Eden區,兩個 Survivor區(from區、to區)。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被複制到from區,當這個區滿時,執行normal gc,此區的存活對象將被複制到to區,當這個區也滿了的時候,執行full gc,從第一個from區複製過來的並且此時還存活的對象,將被複制到“年老區"。

那麼我們如何判斷對象是否需要被移除內存呢?

目前有兩種算法,一種是引用計數器算法,還有一種是可達性分析算法

先來說說引用計數器算法:當對象被引用的時候會在對象計數器上+1,當引用失效的時候計數器-1。但是隨之而來就出現了問題,如果兩個對象互相引用,雖然兩個對象都爲null,但是不會被收回。

        class TestA{
	  public TestB b;

	}
	class TestB{
	  public TestA a;
	}
	public class Main{
	    public static void main(String[] args){
	        A a = new A();
	        B b = new B();
	        a.b=b;
	        b.a=a;
	        a = null;
	        b = null;
	    }
	}
因此現在的java垃圾回收算法多用可達性分析算法。這裏引用一張書上的圖片


object1是“GC ROOT”的對象作爲起始點,因此object2,object3,object4都是可達的。而5,6,7是不可達的,因爲從object1向下搜索形成的引用鏈不可達。因此5,6,7會被判斷爲可回收的對象。而可作爲GC ROOT的可以是:

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

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

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

4.本地方法棧中JNI引用的對象。


 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章