Java垃圾收集機制

jopen 9年前發布 | 29K 次閱讀 Java垃圾收集 Java開發
所謂“ java”,其實不只是一門語言,而是一種設計的美麗,更是一種關于世界觀、關于架構之美的映射。
    一次,所謂的“面試”,讓我開始正視 java的底層架構,一個我所未知的世界,一個我想了解的世界。所以,我開始結識到java虛擬機(JVM),以及java之中的垃圾收集機制。
一、所謂“JVM”
    既然我們時常說到“JVM(java虛擬機)”,那么所謂的java 虛擬機到底是什么嗎?而,我認為他應該是這樣的:
    java虛擬機,Java Virtual Machine,跟我們平常使用的Virtual PC或者VMware一樣,是在操作系統上面運行的,一類模仿操作系統環境,虛構出來的計算機。Java虛擬機有自己完善的硬體架構,如處理器、堆棧、寄 存器等,還具有相應的指令系統。不過,VMware等是屬于系統虛擬機,而JVM是屬于程序虛擬機。
    既然我們都說java虛擬機十分重要,那么他為什么這么重要呢?
    我們都曾弄過java的環境配置,知道,要從Oracle官網下載jdk(java的開發工具包)進行安裝,知道jdk里面含有Jre(Java的運行環 境),知道我們要設置計算機的環境路徑。但是,對于新手來說,可能不知道,我們做這些工作,其實都是在做一件事情——配置java的虛擬機。我們所編寫的 java程序,經過編譯之后,會生成可以在Java虛擬機上運行的字節碼文件。因為每一個可以運行java程序的計算機,都擁有java虛擬機,擁有一樣 的運行環境。所以,在一種平臺下編譯生成的字節碼文件,便可以在不同的平臺下運行。
     綜上可知,JVM的優勢是:屏蔽具體平臺的信息,實現跨平臺開發。
    一般的高級語言如果要在不同的平臺上運行,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機后,Java語言在不同平臺上運行時不需要重新編譯。 Java語言使用模式Java虛擬機屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就可以 在多種平臺上不加修改地運行。Java虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。
    顯然,沒有JVM,java將不會有今天這般的輝煌。那么,JVM具體上是如何工作的呢?
     JVM的工作:
    1、先通過調用jdk之中的java.exe,來讓操作系統裝載jvm。
    2、配置jvm的環境:
        1)創建JVM裝載環境和配置

        2)裝載JVM.dll

        3)初始化JVM.dll并掛界到JNIENV(JNI調用接口)實例

        4)調用JNIENV實例裝載并處理class類

    JVM在整個jdk中處于最底層的,負責操作系統的交互,用來屏蔽操作系統環境,提供一個完整的java運行環境。
    首先是將JVM裝入環境,JVM提供的方式是操作系統的動態鏈接文件。既然是裝載JVM的文件,那么操作系統會在你的path下面找到你的程序的 java.exe,再從里面通過各種調用尋找jvm.dll以及jvm.cfg,將其裝載到操作系統,這樣就可以在java中調用JVM的函數了。這些工 作做完之后就開始運行java程序了。
     Java程序有兩種方式一種是jar包,一種是class.
    運行jar,Java -jar XXX.jar運行的時候
    1、Java.exe調用GetMainClassName函數,該函數先獲得JNIEnv實例,然后調用Java類           Java.util.jar.JarFileJNIEnv中方法getManifest()并從返回的Manifest對象中取 getAttributes(“Main-Class”)的值即jar包中文件:META-INF/MANIFEST.MF指定的Main-Class的 主類名作為運行的主類。
    2、之后main函數會調用Java.c中LoadClass方法裝載該主類(使用JNIEnv實例的FindClass)。main函數直接調用Java.c中LoadClass方法裝載該類。
    如果是執行class方法。main函數直接調用Java.c中LoadClass方法裝載該類。
二、java的垃圾收集機制
    既然,我們已經了解到了何為“JVM”,那么我們也要接著了解一下,JVM是如何進行平時的內存分配的?
    過去的語言(如C語言)要求程序員顯示的分配內存、釋放內存。程序在需要時分配內存,不需要時釋放內存。但是這種做法常常會引起“ 內存泄漏”, 即是說,由于某種原因是分配的內存始終沒有得到釋放。如果該任務不斷地重復,程序最終會耗盡內存并異常終止,至少無法正常的運行下去。相比之下,java 不要求程序員顯式的分配內存和釋放內存,避免了很多的潛在問題。java在創建對象時自動分配內存,并當該對象的引用不存在是釋放這塊內存。
   而這也就是,java垃圾回收機制,java相對于以前的語言的優勢。
     java的垃圾收集機制,gc(garbage collection),是指JVM用于釋放哪些不在會用的對象所占用的內存。,雖說,java語言并沒有要求JVM有gc,也沒有規定gc要如何進行工 作,但是,常用的JVM都有GC,而且,大多數gc都使用類似的算法管理內存,和執行收集操作。
    但是一般的,在java語言之中,判斷一塊內存空間是否符合 垃圾回收器標準的標準只有兩個:
    (1)給對象賦予了控制null,以后再沒有調試過。
    (2)給對象賦予了新值,即重新分配了內存空間。
    注:一塊內存空間符合了垃圾回收器的收集標準,并不意味著這塊內存控件就一定會被垃圾回收器收集。
    雖說,java是自動進行內存的分配與釋放的。但是,我們也可以使用System.gc()方法做到強制執行。但是,并不保證每次調用 System.gc()方法就一定能夠啟動垃圾回收,他只不過會向jvm發出這樣的一個申請,但是當地會不會真正執行垃圾回收,一切都是未知數。
    所以,我們可以在執行system.gc()前,先執行finalize()方法,對對象進行強制釋放內存。雖說,在沒有明確釋放內存志愿的情況下,java也會隱形的調用finalize()方法,終止對象,以此來釋放資源。

三、詳解Java GC的工作原理:

1、 首先來看一下JVM內存結構,它是由堆、棧等部分組成,結構圖如下所示。

Java垃圾收集機制

1)堆

    所有通過new創建的對象的內存都在堆中分配。顯然,如果不對堆中的這些對象內存進行分類的話,是多而雜的。在不進行對象存活時間區分的情況下,每次垃圾 回收都是對整個堆空間進行回收,花費時間相對會長,同時,因為每次回收都需要遍歷所有存活對象,但實際上,對于生命周期長的對象而言,這種遍歷是沒有效果 的,因為可能進行了很多次遍歷,但是他們依舊存在。因此,分代垃圾回收采用分治的思想,進行代的劃分,把不同生命周期的對象放在不同代上,不同代上采用最 適合它的垃圾回收方式進行回收。

Java垃圾收集機制

    年輕代:

    所有新生成的對象首先都是放在年輕代的。年輕代的目標就是盡可能快速的收集掉那些生命周期短的對象。年輕代分三個區。一個Eden區,兩個 Survivor區(一般而言)。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被復制到Survivor區(兩個中的一個),當這個 Survivor區滿時,此區的存活對象將被復制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區復制 過來的并且此時還存活的對象,將被復制“年老區(Tenured)”。需要注意,Survivor的兩個區是對稱的,沒先后關系,所以同一個區中可能同時 存在從Eden復制過來 對象,和從前一個Survivor復制過來的對象,而復制到年老區的只有從第一個Survivor去過來的對象。而且,Survivor區總有一個是空 的。同時,根據程序需要,Survivor區是可以配置為多個的(多于兩個),這樣可以增加對象在年輕代中的存在時間,減少被放到年老代的可能。

    年老代:

    在年輕代中經歷了N次垃圾回收后仍然存活的對象,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。

    持久代:

    用于存放靜態文件,如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate 等,在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。持久代大小通過-XX:MaxPermSize=<N>進行設 置。

2)棧

    每個線程執行每個方法的時候都會在棧中申請一個棧幀,每個棧幀包括局部變量區和操作數棧,用于存放此次方法調用過程中的臨時變量、參數和中間結果。

2. JVM垃圾回收機制

    針對上述的分類,GC有兩種類型:Scavenge GC和Full GC。

    1)Scavenge GC

一般情況下,當新對象生成,并且在Eden申請空間失敗時,就會觸發Scavenge GC,對Eden區域進行GC,清除非存活對象,并且把尚且存活的對象移動到Survivor區。然后整理Survivor的兩個區。這種方式的GC是對 年輕代的Eden區進行,不會影響到年老代。因為大部分對象都是從Eden區開始的,同時Eden區不會分配的很大,所以Eden區的GC會頻繁進行。因 而,一般在這里需要使用速度快、效率高的算法,使Eden去能盡快空閑出來。

    2)Full GC
    對整個堆進行整理,包括Young、Tenured和Perm。因為需要對整個塊進行回收,所以比Scavenge GC要慢,因此應該盡可能減少Full GC的次數。 在對JVM調優的過程中,很大一部分工作就是對于FullGC的調節。有如下原因可能導致Full GC:

    (1) 年老代(Tenured)被寫滿

(2)持久代(Perm)被寫滿

(3)System.gc()被顯示調用

(4)上一次GC之后Heap的各域分配策略動態變化

原文 :http://cyw.iteye.com/blog/2199665

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!