日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java垃圾回收机制算法分析

發(fā)布時(shí)間:2023/12/15 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java垃圾回收机制算法分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原文參考:螞蟻課堂余勝軍老師。

?

垃圾回收機(jī)制概述

 Java語言中一個(gè)顯著的特點(diǎn)就是引入了垃圾回收機(jī)制,使c++程序員最頭疼的內(nèi)存管理的問題迎刃而解,它使得Java程序員在編寫程序的時(shí)候不再需要考慮內(nèi)存管理。由于有個(gè)垃圾回收機(jī)制,Java中的對象不再有“作用域”的概念,只有對象的引用才有“作用域”。垃圾回收可以有效的防止內(nèi)存泄露,有效的使用空閑的內(nèi)存。

  ps:內(nèi)存泄露是指該內(nèi)存空間使用完畢之后未回收,在不涉及復(fù)雜數(shù)據(jù)結(jié)構(gòu)的一般情況下,Java 的內(nèi)存泄露表現(xiàn)為一個(gè)內(nèi)存對象的生命周期超出了程序需要它的時(shí)間長度,我們有時(shí)也將其稱為“對象游離”。

垃圾回收簡要過程

??這里必須點(diǎn)出一個(gè)很重要的誤區(qū):不可達(dá)的對象并不會(huì)馬上就會(huì)被直接回收,而是至少要經(jīng)過兩次標(biāo)記的過程。?
????????第一次被標(biāo)記過的對象,會(huì)檢查該對象是否重寫了finalize()方法。如果重寫了該方法,則將其放入一個(gè)F-Queue隊(duì)列中,否則,直接將對象加入“即將回收”集合。在第二次標(biāo)記之前,F-Queue隊(duì)列中的所有對象會(huì)逐個(gè)執(zhí)行finalize()方法,但是不保證該隊(duì)列中所有對象的finalize()方法都能被執(zhí)行,這是因?yàn)镴VM創(chuàng)建一個(gè)低優(yōu)先級(jí)的線程去運(yùn)行此隊(duì)列中的方法,很可能在沒有遍歷完之前,就已經(jīng)被剝奪了運(yùn)行的權(quán)利。那么運(yùn)行finalize()方法的意義何在呢?這是對象避免自己被清理的最后手段:如果在執(zhí)行finalize()方法的過程中,使得此對象重新與GC Roots引用鏈相連,則會(huì)在第二次標(biāo)記過程中將此對象從F-Queue隊(duì)列中清除,避免在這次回收中被清除,恢復(fù)成了一個(gè)“正常”的對象。但顯然這種好事不能無限的發(fā)生,對于曾經(jīng)執(zhí)行過一次finalize()的對象來說,之后如果再被標(biāo)記,則不會(huì)再執(zhí)行finalize()方法,只能等待被清除的命運(yùn)。?
????????之后,GC將對F-Queue中的對象進(jìn)行第二次小規(guī)模的標(biāo)記,將隊(duì)列中重新與GC Roots引用鏈恢復(fù)連接的對象清除出“即將回收”集合。所有此集合中的內(nèi)容將被回收。

手動(dòng)GC回收

public class JVMDemo05 {

??? public static void main(String[] args) {

??????? JVMDemo05 jvmDemo05 = new JVMDemo05();

??????? //jvmDemo05 = null;

??????? System.gc();

??? }

??? protected void finalize() throws Throwable {

?????? System.out.println("gc在回收對象...");

??? }

}

?

finalize作用

Java技術(shù)使用finalize()方法在垃圾收集器將對象從內(nèi)存中清除出去前,做必要的清理工作。這個(gè)方法是由垃圾收集器在確定這個(gè)對象沒有被引用時(shí)對這個(gè)對象調(diào)用的。它是在Object類中定義的,因此所有的類都繼承了它。子類覆蓋finalize()方法以整理系統(tǒng)資源或者執(zhí)行其他清理工作。finalize()方法是在垃圾收集器刪除對象之前對這個(gè)對象調(diào)用的。

內(nèi)存泄露

內(nèi)存泄漏的定義:對象已經(jīng)沒有被應(yīng)用程序使用,但是垃圾回收器沒辦法移除它們因?yàn)檫€在被引用著

要想理解這個(gè)定義,我們需要先了解一下對象在內(nèi)存中的狀態(tài)。下面的這張圖就解釋了什么是無用對象以及什么是未被引用對象。

上面圖中可以看出,里面有被引用對象和未被引用對象。未被引用對象會(huì)被垃圾回收器回收,而被引用的對象卻不會(huì)。未被引用的對象當(dāng)然是不再被使用的對象,因?yàn)闆]有對象再引用它。然而無用對象卻不全是未被引用對象。其中還有被引用的。就是這種情況導(dǎo)致了內(nèi)存泄漏。

如何防止內(nèi)存泄露

下面是幾條容易上手的建議,來幫助你防止內(nèi)存泄漏的發(fā)生。

  • 特別注意一些像HashMap、ArrayList的集合對象,它們經(jīng)常會(huì)引發(fā)內(nèi)存泄漏。當(dāng)它們被聲明為static時(shí),它們的生命周期就會(huì)和應(yīng)用程序一樣長。
  • 特別注意事件監(jiān)聽和回調(diào)函數(shù)。當(dāng)一個(gè)監(jiān)聽器在使用的時(shí)候被注冊,但不再使用之后卻未被反注冊。
  • “如果一個(gè)類自己管理內(nèi)存,那開發(fā)人員就得小心內(nèi)存泄漏問題了。” 通常一些成員變量引用其他對象,初始化的時(shí)候需要置空。

垃圾回收機(jī)制算法

引用計(jì)數(shù)法

1.1概述

給對象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加1;當(dāng)引用失效時(shí),計(jì)數(shù)器值就減1;任何時(shí)刻計(jì)數(shù)器都為0的對象就是不再被使用的,垃圾收集器將回收該對象使用的內(nèi)存。

1.2優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

引用計(jì)數(shù)收集器可以很快的執(zhí)行,交織在程序運(yùn)行中。對程序需要不被長時(shí)間打斷的實(shí)時(shí)環(huán)境比較有利。

缺點(diǎn):

無法檢測出循環(huán)引用。如父對象有一個(gè)對子對象的引用,子對象反過來引用父對象。這樣,他們的引用計(jì)數(shù)永遠(yuǎn)不可能為0.而且每次加減非常浪費(fèi)內(nèi)存。

復(fù)制算法

s0和s1將可用內(nèi)存按容量分成大小相等的兩塊,每次只使用其中一塊當(dāng)這塊內(nèi)存使用完了,就將還存活的對象復(fù)制到另一塊內(nèi)存上去,然后把使用過的內(nèi)存空間一次清理掉這樣使得每次都是對其中一塊內(nèi)存進(jìn)行回收,內(nèi)存分配時(shí)不用考慮內(nèi)存碎片等復(fù)雜情況,只需要移動(dòng)堆頂指針,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡單,運(yùn)行高效。

復(fù)制算法的缺點(diǎn)顯而易見,可使用的內(nèi)存降為原來一半。

注:復(fù)制算法用于在新生代垃圾回收(s0,s1區(qū))

?

標(biāo)記清除算法

標(biāo)記-清除(Mark-Sweep)算法顧名思義,主要就是兩個(gè)動(dòng)作,一個(gè)是標(biāo)記,另一個(gè)就是清除。

標(biāo)記就是根據(jù)特定的算法(如:引用計(jì)數(shù)算法,可達(dá)性分析算法等)標(biāo)出內(nèi)存中哪些對象可以回收,哪些對象還要繼續(xù)用。

標(biāo)記指示回收(不可達(dá)),那就直接收掉;標(biāo)記指示對象還能用(可達(dá)),那就原地不動(dòng)留下。

缺點(diǎn)

  • 標(biāo)記與清除沒有連續(xù)性效率低;
  • 清除之后內(nèi)存會(huì)產(chǎn)生大量碎片;
  • ?

    標(biāo)記-壓縮算法

    標(biāo)記壓縮法在標(biāo)記清除基礎(chǔ)之上做了優(yōu)化,把存活的對象壓縮到內(nèi)存一端,而后進(jìn)行垃圾清理。(java中老年代使用的就是標(biāo)記壓縮法)

    分代收集算法

    根據(jù)內(nèi)存中對象的存活周期不同,將內(nèi)存劃分為幾塊,java的虛擬機(jī)中一般把內(nèi)存劃分為新生代和年老代,當(dāng)新創(chuàng)建對象時(shí)一般在新生代中分配內(nèi)存空間,當(dāng)新生代垃圾收集器回收幾次之后仍然存活的對象會(huì)被移動(dòng)到年老代內(nèi)存中,當(dāng)大對象在新生代中無法找到足夠的連續(xù)內(nèi)存時(shí)也直接在年老代中創(chuàng)建

    對于新生代和老年代來說,新生代回收頻率很高,但是每次回收耗時(shí)很短,而老年代回收頻率較低,但是耗時(shí)會(huì)相對較長,所以應(yīng)該盡量減少老年代的GC.

    ?

    垃圾回收時(shí)的停頓現(xiàn)象

    垃圾回收的任務(wù)是識(shí)別和回收垃圾對象進(jìn)行內(nèi)存清理,為了讓垃圾回收器可以更高效的執(zhí)行,大部分情況下,會(huì)要求系統(tǒng)進(jìn)入一個(gè)停頓的狀態(tài)。停頓的目的是為了暫停所有的應(yīng)用線程,只有這樣系統(tǒng)才不會(huì)有新垃圾的產(chǎn)生。同時(shí)停頓保證了系統(tǒng)狀態(tài)在某一個(gè)瞬間的一致性,也有利于更好的標(biāo)記垃圾對象。因此在垃圾回收時(shí),都會(huì)產(chǎn)生應(yīng)用程序的停頓。

    垃圾收集器

    什么是Java垃圾回收器

    Java垃圾回收器是Java虛擬機(jī)(JVM)的三個(gè)重要模塊(另外兩個(gè)是解釋器和多線程機(jī)制)之一,為應(yīng)用程序提供內(nèi)存的自動(dòng)分配(Memory Allocation)、自動(dòng)回收(Garbage Collect)功能,這兩個(gè)操作都發(fā)生在Java堆上(一段內(nèi)存快)。某一個(gè)時(shí)點(diǎn),一個(gè)對象如果有一個(gè)以上的引用(Rreference)指向它,那么該對象就是活著的(Live),否則死亡(Dead),視為垃圾,可被垃圾回收器回收再利用。垃圾回收操作需要消耗CPU、線程、時(shí)間等資源,所以容易理解的是垃圾回收操作不是實(shí)時(shí)的發(fā)生(即并不是對象死亡馬上釋放),當(dāng)內(nèi)存消耗完或者是達(dá)到某一個(gè)指標(biāo)(Threshold,使用內(nèi)存占總內(nèi)存的比列,比如0.75)時(shí),觸發(fā)垃圾回收操作。有一個(gè)對象死亡的例外,java.lang.Thread類型的對象即使沒有引用,只要線程還在運(yùn)行,就不會(huì)被回收。

    串行回收器(Serial Collector)

    單線程執(zhí)行回收操作,回收期間暫停所有應(yīng)用線程的執(zhí)行,client模式下的默認(rèn)回收器,通過-XX:+UseSerialGC命令行可選項(xiàng)強(qiáng)制指定。參數(shù)可以設(shè)置使用新生代串行和老年代串行回收器。

    年輕代的回收算法(Minor Collection)
    把Eden區(qū)的存活對象移到To區(qū),To區(qū)裝不下直接移到年老代,把From區(qū)的移到To區(qū),To區(qū)裝不下直接移到年老代,From區(qū)里面年齡很大的升級(jí)到年老代。 回收結(jié)束之后,Eden和From區(qū)都為空,此時(shí)把From和To的功能互換,From變To,To變From,每一輪回收之前To都是空的。設(shè)計(jì)的選型為復(fù)制。

    年老代的回收算法(Full Collection)
    年老代的回收分為三個(gè)步驟,標(biāo)記(Mark)、清除(Sweep)、合并(Compact)。標(biāo)記階段把所有存活的對象標(biāo)記出來,清除階段釋放所有死亡的對象,合并階段 把所有活著的對象合并到年老代的前部分,把空閑的片段都留到后面。設(shè)計(jì)的選型為合并,減少內(nèi)存的碎片。

    并行回收

    并行回收器(ParNew回收器)


    并行回收器在串行回收器基礎(chǔ)上做了改進(jìn),他可以使用多個(gè)線程同時(shí)進(jìn)行垃
    圾回收
    ,對于計(jì)算能力強(qiáng)的計(jì)算機(jī)而言,可以有效的縮短垃圾回收所需的尖
    際時(shí)間。
    ParNew 回收器是一個(gè)工作在新生代的垃圾回收器,它只是簡單地將串行回收器多線程化 ,它的回收策略,算法以及參數(shù)和新生代串行回收器一樣。
    使用-XX:+UseParNewGC 新生代ParNew回收器,老年代則使用串行回收器,
    ParNew回收器工作時(shí)的線程數(shù)量可以使用XX:ParaleiGCThreads參數(shù)指
    定,一般最好和計(jì)算機(jī)的CPU相當(dāng),避免過多的栽程影響性能。

    并行回收集器(ParallelGC回收器)

    老年代ParallelOldGC回收器也是一種多線程的回收器,和新生代的
    ParallelGC回收器一樣,也是一種關(guān)往吞吐量的回收器,他使用了標(biāo)記壓縮
    算法進(jìn)行實(shí)現(xiàn)。
    -XX:+UseParallelOldGC?進(jìn)行設(shè)置
    -XX:+ParallelCThread也可以設(shè)置垃圾收集時(shí)的線程教量。

    ?

    CMS(并發(fā)GC)收集器

    CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。CMS收集器是基于標(biāo)記-清除算法實(shí)現(xiàn)的,整個(gè)收集過程大致分為4個(gè)步驟:

    ①.初始標(biāo)記(CMS initial mark)

    ②.并發(fā)標(biāo)記(CMS concurrenr mark)

    ③.重新標(biāo)記(CMS remark)

    ④.并發(fā)清除(CMS concurrent sweep)

    ? ? ?其中初始標(biāo)記、重新標(biāo)記這兩個(gè)步驟仍然需要停頓其他用戶線程。初始標(biāo)記僅僅只是標(biāo)記出GC ROOTS能直接關(guān)聯(lián)到的對象,速度很快,并發(fā)標(biāo)記階段是進(jìn)行GC ROOTS 根搜索算法階段,會(huì)判定對象是否存活。而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對象的標(biāo)記記錄,這個(gè)階段的停頓時(shí)間會(huì)被初始標(biāo)記階段稍長,但比并發(fā)標(biāo)記階段要短。

    ? ? ?由于整個(gè)過程中耗時(shí)最長的并發(fā)標(biāo)記和并發(fā)清除過程中,收集器線程都可以與用戶線程一起工作,所以整體來說,CMS收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)執(zhí)行的。

    CMS收集器的優(yōu)點(diǎn):并發(fā)收集、低停頓,但是CMS還遠(yuǎn)遠(yuǎn)達(dá)不到完美,CMS收集器主要有三個(gè)顯著缺點(diǎn):

    CMS收集器對CPU資源非常敏感。在并發(fā)階段,雖然不會(huì)導(dǎo)致用戶線程停頓,但是會(huì)占用CPU資源而導(dǎo)致引用程序變慢,總吞吐量下降。CMS默認(rèn)啟動(dòng)的回收線程數(shù)是:(CPU數(shù)量+3) / 4。

    CMS收集器無法處理浮動(dòng)垃圾,可能出現(xiàn)“Concurrent Mode Failure“,失敗后而導(dǎo)致另一次Full GC的產(chǎn)生。由于CMS并發(fā)清理階段用戶線程還在運(yùn)行,伴隨程序的運(yùn)行自然會(huì)有新的垃圾不斷產(chǎn)生,這一部分垃圾出現(xiàn)在標(biāo)記過程之后,CMS無法在本次收集中處理它們,只好留待下一次GC時(shí)將其清理掉。這一部分垃圾稱為“浮動(dòng)垃圾”。也是由于在垃圾收集階段用戶線程還需要運(yùn)行,即需要預(yù)留足夠的內(nèi)存空間給用戶線程使用,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進(jìn)行收集,需要預(yù)留一部分內(nèi)存空間提供并發(fā)收集時(shí)的程序運(yùn)作使用。在默認(rèn)設(shè)置下,CMS收集器在老年代使用了68%的空間時(shí)就會(huì)被激活,也可以通過參數(shù)-XX:CMSInitiatingOccupancyFraction的值來提供觸發(fā)百分比,以降低內(nèi)存回收次數(shù)提高性能。要是CMS運(yùn)行期間預(yù)留的內(nèi)存無法滿足程序其他線程需要,就會(huì)出現(xiàn)“Concurrent Mode Failure”失敗,這時(shí)候虛擬機(jī)將啟動(dòng)后備預(yù)案:臨時(shí)啟用Serial Old收集器來重新進(jìn)行老年代的垃圾收集,這樣停頓時(shí)間就很長了。所以說參數(shù)-XX:CMSInitiatingOccupancyFraction設(shè)置的過高將會(huì)很容易導(dǎo)致“Concurrent Mode Failure”失敗,性能反而降低。

    最后一個(gè)缺點(diǎn),CMS是基于“標(biāo)記-清除”算法實(shí)現(xiàn)的收集器,使用“標(biāo)記-清除”算法收集后,會(huì)產(chǎn)生大量碎片。空間碎片太多時(shí),將會(huì)給對象分配帶來很多麻煩,比如說大對象,內(nèi)存空間找不到連續(xù)的空間來分配不得不提前觸發(fā)一次Full GC。為了解決這個(gè)問題,CMS收集器提供了一個(gè)-XX:UseCMSCompactAtFullCollection開關(guān)參數(shù),用于在Full GC之后增加一個(gè)碎片整理過程,還可通過-XX:CMSFullGCBeforeCompaction參數(shù)設(shè)置執(zhí)行多少次不壓縮的Full GC之后,跟著來一次碎片整理過程。

    G1回收器

    G1回收器(Garbage-First)實(shí)在jdk1.7中提出的垃圾回收器,從長期目標(biāo)來看是為了取
    代CMS回收器,G1回收器擁有獨(dú)特的垃圾回收策略,G1屬于分代垃圾回收器,區(qū)分
    新生代和老年代,依然有eden和from/to區(qū),它并不要求整個(gè)eden區(qū)或者新生代、老
    年代的空間都連續(xù),它使用了分區(qū)算法。
    并行性:?G1回收期間可多線程同時(shí)工作。
    并發(fā)性:G1擁有與應(yīng)用程序交替執(zhí)行能力,部分工作可與應(yīng)用程序同時(shí)執(zhí)行,在整個(gè)
    GC期間不會(huì)完全阻塞應(yīng)用程序。
    分代GC: G1依然是一個(gè)分代的收集器,但是它是非兩新生代和老年代一杯政的雜尊。
    空間整理:G1在回收過程中,不會(huì)像CMS那樣在若干次GC后要進(jìn)行碎片整理。G1采用了有效復(fù)制對象的方式,減少空間碎片。
    可預(yù)見性:由于分區(qū)的原因,G1可以只選取部分區(qū)域進(jìn)行回收,縮小了回收的范圍,提升了性能。
    使用-XX:+UseG1GC應(yīng)用G1收集器

    使用-XX:MaxGCPausel指定最大停頓時(shí)間(Mills)

    使用-XX:ParallelGCThreads設(shè)置并行回收的線程數(shù)量

    總結(jié)

    初始堆值和最大堆內(nèi)存內(nèi)存越大,吞吐量就越高。

    最好使用并行收集器,因?yàn)椴⑿惺占魉俣缺却型掏铝扛?#xff0c;速度快。

    設(shè)置堆內(nèi)存新生代的比例和老年代的比例最好為1:2或者1:3。

    減少GC對老年代的回收。

    總結(jié)

    以上是生活随笔為你收集整理的java垃圾回收机制算法分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。