溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java垃圾回收機(jī)制怎么理解

發(fā)布時(shí)間:2022-01-17 16:29:11 來(lái)源:億速云 閱讀:97 作者:iii 欄目:編程語(yǔ)言

這篇文章主要講解了“Java垃圾回收機(jī)制怎么理解”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Java垃圾回收機(jī)制怎么理解”吧!

Java垃圾回收是一個(gè)自動(dòng)運(yùn)行的管理程序運(yùn)行時(shí)使用的內(nèi)存的進(jìn)程。通過GC的自動(dòng)執(zhí)行JVM將程序員從申請(qǐng)和釋放內(nèi)存的繁重操作中解放出來(lái)。

Java垃圾回收GC初始化

作為一個(gè)自動(dòng)執(zhí)行的進(jìn)程,程序員不需要在代碼中主動(dòng)初始化GC。Java提供了System.gc()和Runtime.gc()這兩個(gè)hook來(lái)請(qǐng)求JVM調(diào)用GC進(jìn)程。

盡管要求系統(tǒng)機(jī)制給程序員提供調(diào)用GC的機(jī)會(huì),但是實(shí)際上這是由JVM負(fù)責(zé)決定的。JVM可以選擇拒絕啟動(dòng)GC的請(qǐng)求,因此并不保證這些請(qǐng)求會(huì)真的調(diào)用垃圾回收。這是JVM基于內(nèi)存堆空間的Eden區(qū)的使用情況做出的決定。JVM規(guī)范將這個(gè)選擇權(quán)利留給了各個(gè)JVM的具體實(shí)現(xiàn),因此實(shí)際上JVM是如何選擇的視不同JVM的實(shí)現(xiàn)而定(不過要記住的是,不能依賴于這兩個(gè)方法的調(diào)用,它們是不被保證執(zhí)行的)。

毫無(wú)疑問的是,我們知道垃圾回收進(jìn)程是不能強(qiáng)制執(zhí)行的。不過我剛發(fā)現(xiàn)一個(gè)調(diào)用System.gc()確實(shí)有意義的場(chǎng)景??聪逻@篇文章你就會(huì)了解System.gc()調(diào)用是可用的這個(gè)特殊的場(chǎng)景。

Java 垃圾回收進(jìn)程

垃圾回收是一個(gè)回收不再使用的內(nèi)存空間并將它變成能夠?yàn)閷?lái)的實(shí)例使用的過程。

Eden Space:當(dāng)一個(gè)實(shí)例被創(chuàng)建的時(shí)候,它最初被存放在堆內(nèi)存空間的年輕代的Eden區(qū)中。

注意:如果您不太理解這些術(shù)語(yǔ),建議您先看下介紹內(nèi)存模型、JVM架構(gòu)及這些術(shù)語(yǔ)的詳細(xì)解釋的文章:garbage-collection-introduction-tutorial

Survivor Space(S0  和S1):作為minor回收周期的一部分,還活著的對(duì)象(還有引用指向它)被從eden區(qū)中移動(dòng)到survivor空間S0。同樣的,垃圾回收器掃描S0并將活著的實(shí)例移動(dòng)到S1。

無(wú)用的對(duì)象(沒有引用指向)被標(biāo)記并回收。垃圾回收器(有四種可用的垃圾回收器,將在下一篇文章中介紹)決定這些被標(biāo)記的實(shí)例是在掃描的過程中移出內(nèi)存還是在另外獨(dú)立的遷移進(jìn)程中執(zhí)行。

Old Generation:老年代或者***代是堆內(nèi)存的第二個(gè)邏輯部分。當(dāng)垃圾回收器在做minor GC周期中,S1  survivor區(qū)中還活著的實(shí)例會(huì)被提升到老年代中。S1區(qū)中不再被引用的對(duì)象被標(biāo)記并清除。

Major GC:在Java垃圾回收過程中實(shí)例生命周期的***一個(gè)階段。Major GC在垃圾回收過程中掃描屬于Old  Generation部分的堆內(nèi)存。如果實(shí)例沒有被任何引用關(guān)聯(lián),它們將被標(biāo)記、清除;如果它們還被引用關(guān)聯(lián)著,則將繼續(xù)存留在old generation。

Memory

Fragmentation:一旦實(shí)例從堆內(nèi)存中刪除了,它們?cè)瓉?lái)的位置將空出來(lái)給以后分配實(shí)例使用。顯然這些空閑空間很容易在內(nèi)存空間中產(chǎn)生碎片。為了能夠更快地分配實(shí)例地址,需要對(duì)內(nèi)存做去碎片化操作。根據(jù)不同垃圾回收器的策略,被回收的內(nèi)存將在回收的過程同時(shí)或者在GC另外獨(dú)立的過程中壓縮整合。

垃圾回收過程中的對(duì)象銷毀–Finalization

就在移除一個(gè)對(duì)象并回收它的內(nèi)存空間之前,Java垃圾回收器將會(huì)調(diào)用各個(gè)實(shí)例的finalize()方法,這樣實(shí)例對(duì)象就有機(jī)會(huì)可以釋放掉它占用的資源。盡管finalize()方法是保證在回收內(nèi)存空間之前執(zhí)行的,但是對(duì)具體的執(zhí)行時(shí)間和執(zhí)行順序是沒有任何保證的。多個(gè)實(shí)例之間的finalize()執(zhí)行順序是不能提前預(yù)知的,甚至有可能它們是并行執(zhí)行的。程序不應(yīng)該預(yù)先假設(shè)實(shí)例執(zhí)行finalize()的方法,也不應(yīng)該使用finalize()方法來(lái)回收資源。

  1. 在finalize過程中拋出的任何異常都默認(rèn)被忽略掉了,同時(shí)對(duì)象的銷毀過程被取消

  2. JVM規(guī)范并沒有討論關(guān)于弱引用的垃圾回收,這是明確聲明的。具體的細(xì)節(jié)留給實(shí)現(xiàn)者決定。

  3. 垃圾回收是由守護(hù)進(jìn)程執(zhí)行的

對(duì)象何時(shí)變成可被垃圾回收的?

  • 所有不能被活著的線程到達(dá)實(shí)例

  • 不能被其他對(duì)象到達(dá)的循環(huán)引用對(duì)象 Java中有多種不同的引用類型。實(shí)例的可回收性取決于它的引用類型。

Java垃圾回收機(jī)制怎么理解

在編譯過程中Java編譯器有個(gè)優(yōu)化機(jī)制,編譯器可以選擇將null賦值給一個(gè)實(shí)例,這樣就將這個(gè)實(shí)例標(biāo)志為可被回收的。

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!");        }    }

在上面這個(gè)類中,實(shí)例lion在除了初始化那一行在其他地方都沒有被使用到。因此作為一種優(yōu)化方法,Java編譯器可以在初始化那一行后面立即賦值lion =  null。這樣finlizer可能會(huì)在Main方法的SOP之前打印結(jié)果。

Rest in Peace!  Main is completed.

但結(jié)果的順序是不確定的,它取決于JVM的實(shí)現(xiàn)以及運(yùn)行時(shí)的內(nèi)存使用情況。從中我們能知道的一點(diǎn)是:編譯器在發(fā)現(xiàn)一個(gè)實(shí)例的之后的程序中不再被引用時(shí)可以選擇提前釋放實(shí)例內(nèi)存。

  • 這里有個(gè)實(shí)例何時(shí)變成可回收更好的例子。實(shí)例所有的屬性可以被存儲(chǔ)在寄存器中之后可以從寄存器中讀取這些屬性值,且未來(lái)在任何情況下都不會(huì)將值寫回到實(shí)例對(duì)象中。這樣盡管這個(gè)實(shí)例在未來(lái)還是被使用到了,但是實(shí)例對(duì)象依然可以被標(biāo)記為可回收的。

  • 何時(shí)能被垃圾回收可以簡(jiǎn)單到僅僅認(rèn)為在賦值為null的時(shí)候也可以復(fù)雜到如上面那一點(diǎn)所說的那樣。JVM的實(shí)現(xiàn)者會(huì)做一些取舍。其目標(biāo)都是希望留下最少的痕跡,提高響應(yīng)時(shí)間增大吞吐量。為了能夠達(dá)到這些目的,JVM實(shí)現(xiàn)者可以在垃圾回收中選擇更好的模式或算法來(lái)回收內(nèi)存。

  • 當(dāng)finalize()被調(diào)用的時(shí)候,JVM釋放掉當(dāng)前線程的所有同步塊。

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();             //沒有任何一個(gè)對(duì)象是可以被GC的             t1.t = t2;//沒有任何一個(gè)對(duì)象是可以被GC的             t2.t = t3;//沒有任何一個(gè)對(duì)象是可以被GC的             t3.t = t1;//沒有任何一個(gè)對(duì)象是可以被GC的              t1 = null;//沒有任何一個(gè)對(duì)象是可以被GC的,t3.t還有對(duì)t1的引用              t2 = null;//沒有任何一個(gè)對(duì)象是可以被GC的,t3.t.t還有對(duì)t2的引用             t3 = null;//所有3個(gè)對(duì)象都可以被GC(沒有一個(gè)被引用了)             //只有各個(gè)對(duì)象的變量t互相循環(huán)引用形成了一個(gè)孤立的引用環(huán),而沒有外部引用         }          protected void finalize() {             System.out.println("Garbage collected from boject" + i);             i++;         }     }

Example Program for GC OutOfMemoryError

垃圾回收機(jī)制并不保證發(fā)生內(nèi)存溢出時(shí)的安全,事實(shí)上內(nèi)存溢出將會(huì)導(dǎo)致程序的崩潰,拋出OutOfMemoryError。import java.util.LinkedList; 

import java.util.List;  public class GC {     public static void main(String[] args[]) {         List l = new LinkedList();         //進(jìn)入內(nèi)部***循環(huán)直接向鏈表中不斷添加元素         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)

感謝各位的閱讀,以上就是“Java垃圾回收機(jī)制怎么理解”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Java垃圾回收機(jī)制怎么理解這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI