細述 Java垃圾回收機制→How Java Garbage Collection Works?

本文非原創,翻譯自How Java Garbage Collection Works?
在Java中爲對象分配和釋放內存空間都是由垃圾回收線程自動執行完成的。和C語言不同的是Java程序員不須要手動寫垃圾回收相關的代碼。這是使得Java如此流行,同時也是Java能幫助程序員寫出更好的Java應用的優勢之一。java

這是垃圾回收機制系列文章的第二篇。但願您已經讀過了第一部分Java垃圾回收簡介.程序員

Java垃圾回收是一個自動運行的管理程序運行時使用的內存的進程。經過GC的自動執行JVM將程序員從申請和釋放內存的繁重操做中解放出來。算法

Java垃圾回收GC初始化

做爲一個自動執行的進程,程序員不須要在代碼中主動初始化GC。Java提供了System.gc()和Runtime.gc()這兩個hook來請求JVM調用GC進程。segmentfault

儘管要求系統機制給程序員提供調用GC的機會,可是實際上這是由JVM負責決定的。JVM能夠選擇拒絕啓動GC的請求,所以並不保證這些請求會真的調用垃圾回收。這是JVM基於內存堆空間的Eden區的使用狀況作出的決定。JVM規範將這個選擇權利留給了各個JVM的具體實現,所以實際上JVM是如何選擇的視不一樣JVM的實現而定(不過要記住的是,不能依賴於這兩個方法的調用,它們是不被保證執行的)。安全

毫無疑問的是,咱們知道垃圾回收進程是不能強制執行的。不過我剛發現一個調用System.gc()確實有意義的場景。看下這篇文章你就會了解System.gc()調用是可用的這個特殊的場景。架構

Java 垃圾回收進程

垃圾回收是一個回收再也不使用的內存空間並將它變成可以爲未來的實例使用的過程。優化

Java 垃圾回收進程
Eden Space:當一個實例被建立的時候,它最初被存放在堆內存空間的年輕代的Eden區中。ui

注意:若是您不太理解這些術語,建議您先看下介紹內存模型、JVM架構及這些術語的詳細解釋的文章:garbage-collection-introduction-tutorial
Survivor Space(S0 和S1):做爲minor回收週期的一部分,還活着的對象(還有引用指向它)被從eden區中移動到survivor空間S0。一樣的,垃圾回收器掃描S0並將活着的實例移動到S1。spa

無用的對象(沒有引用指向)被標記並回收。垃圾回收器(有四種可用的垃圾回收器,將在下一篇文章中介紹)決定這些被標記的實例是在掃描的過程當中移出內存仍是在另外獨立的遷移進程中執行。線程

Old Generation:老年代或者永久代是堆內存的第二個邏輯部分。當垃圾回收器在作minor GC週期中,S1 survivor區中還活着的實例會被提高到老年代中。S1區中再也不被引用的對象被標記並清除。

Major GC:在Java垃圾回收過程當中實例生命週期的最後一個階段。Major GC在垃圾回收過程當中掃描屬於Old Generation部分的堆內存。若是實例沒有被任何引用關聯,它們將被標記、清除;若是它們還被引用關聯着,則將繼續存留在old generation。

Memory
Fragmentation:一旦實例從堆內存中刪除了,它們原來的位置將空出來給之後分配實例使用。顯然這些空閒空間很容易在內存空間中產生碎片。爲了可以更快地分配實例地址,須要對內存作去碎片化操做。根據不一樣垃圾回收器的策略,被回收的內存將在回收的過程同時或者在GC另外獨立的過程當中壓縮整合。

垃圾回收過程當中的對象銷燬–Finalization

就在移除一個對象並回收它的內存空間以前,Java垃圾回收器將會調用各個實例的finalize()方法,這樣實例對象就有機會能夠釋放掉它佔用的資源。儘管finalize()方法是保證在回收內存空間以前執行的,可是對具體的執行時間和執行順序是沒有任何保證的。多個實例之間的finalize()執行順序是不能提早預知的,甚至有可能它們是並行執行的。程序不該該預先假設實例執行finalize()的方法,也不該該使用finalize()方法來回收資源。

  • 在finalize過程當中拋出的任何異常都默認被忽略掉了,同時對象的銷燬過程被取消

  • JVM規範並無討論關於弱引用的垃圾回收,這是明確聲明的。具體的細節留給實現者決定。

  • 垃圾回收是由守護進程執行的

對象什麼時候變成可被垃圾回收的?

  • 全部不能被活着的線程到達實例

  • 不能被其餘對象到達的循環引用對象 Java中有多種不一樣的引用類型。實例的可回收性取決於它的引用類型。

Reference Garbage Collection
Strong Refrence 不被垃圾回收
Soft Reference 做爲最後的選擇,有可能被回收
Weak Reference 能夠被垃圾回收
Phantom Reference 能夠被垃圾回收

在編譯過程當中Java編譯器有個優化機制,編譯器能夠選擇將null賦值給一個實例,這樣就將這個實例標誌爲可被回收的。

class Animal {
        public static void main(String[] args) {
            Animal lion = new Animal();
            System.out.println("Main is completed.");
        }

        protected void finalize() {
            System.out.println("Rest in Peace!");
        }
    }

在上面這個類中,實例lion在除了初始化那一行在其餘地方都沒有被使用到。所以做爲一種優化方法,Java編譯器能夠在初始化那一行後面當即賦值lion = null。這樣finlizer可能會在Main方法的SOP以前打印結果。

Rest in Peace!
Main is completed.

但結果的順序是不肯定的,它取決於JVM的實現以及運行時的內存使用狀況。從中咱們能知道的一點是:編譯器在發現一個實例的以後的程序中再也不被引用時能夠選擇提早釋放實例內存。

  • 這裏有個實例什麼時候變成可回收更好的例子。實例全部的屬性能夠被存儲在寄存器中以後能夠從寄存器中讀取這些屬性值,且將來在任何狀況下都不會將值寫回到實例對象中。這樣儘管這個實例在將來仍是被使用到了,可是實例對象依然能夠被標記爲可回收的。

  • 什麼時候能被垃圾回收能夠簡單到僅僅認爲在賦值爲null的時候也能夠複雜到如上面那一點所說的那樣。JVM的實現者會作一些取捨。其目標都是但願留下最少的痕跡,提升響應時間增大吞吐量。爲了可以達到這些目的,JVM實現者能夠在垃圾回收中選擇更好的模式或算法來回收內存。

  • 當finalize()被調用的時候,JVM釋放掉當前線程的全部同步塊。

Example Program for GC Scope

class GCScope {
        GCScope t;
        static int i = 1;

        public static void main(String args[]) {
            GCScope t1 = new GCScope();
            GCScope t2 = new GCScope();
            GCScope t3 = new GCScope();
            //沒有任何一個對象是能夠被GC的
            t1.t = t2;//沒有任何一個對象是能夠被GC的
            t2.t = t3;//沒有任何一個對象是能夠被GC的
            t3.t = t1;//沒有任何一個對象是能夠被GC的

            t1 = null;//沒有任何一個對象是能夠被GC的,t3.t還有對t1的引用

            t2 = null;//沒有任何一個對象是能夠被GC的,t3.t.t還有對t2的引用
            t3 = null;//全部3個對象均可以被GC(沒有一個被引用了)
            //只有各個對象的變量t互相循環引用造成了一個孤立的引用環,而沒有外部引用
        }

        protected void finalize() {
            System.out.println("Garbage collected from boject" + i);
            i++;
        }
    }

Example Program for GC OutOfMemoryError
垃圾回收機制並不保證發生內存溢出時的安全,事實上內存溢出將會致使程序的崩潰,拋出OutOfMemoryError。

import java.util.LinkedList;
import java.util.List;

public class GC {
    public static void main(String[] args[]) {
        List l = new LinkedList();
        //進入內部無限循環直接向鏈表中不斷添加元素
        do {
            l.add(new String("Hello, World!");
        } while (true);
    }
}

Output

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.LinkedList.linkLast(LinkedList.java:142)
    at java.util.LinkedList.add(LinkedList.java:338)
    at com.javapapers.java.GCScope.main(GCScope.java:12)
相關文章
相關標籤/搜索