偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

JVM 判斷對(duì)象已死,實(shí)踐驗(yàn)證GC回收

云計(jì)算 虛擬化
在這趟車上有人下、有人上,外在別人給你點(diǎn)評(píng)的標(biāo)簽、留下的烙印,都只是這趟車上的故事。只有個(gè)人成長(zhǎng)了、積累了、沉淀了,才有機(jī)會(huì)當(dāng)自己的司機(jī)。

 [[377367]]

本文轉(zhuǎn)載自微信公眾號(hào)「 bugstack蟲(chóng)洞?!?,作者小傅哥  。轉(zhuǎn)載本文請(qǐng)聯(lián)系 bugstack蟲(chóng)洞棧公眾號(hào)。

目錄

  • 一、前言
  • 二、面試題
  • 三、先動(dòng)手驗(yàn)證垃圾回收
  • 四、JVM 垃圾回收知識(shí)框架
    • 1. 判斷對(duì)象已死
    • 2. 垃圾回收算法
    • 3. 垃圾回收器
  • 五、總結(jié)
  • 六、系列推薦

一、前言

提升自身價(jià)值有多重要?

經(jīng)過(guò)了風(fēng)風(fēng)雨雨,看過(guò)了男男女女。時(shí)間經(jīng)過(guò)的歲月就沒(méi)有永恒不變的!

在這趟車上有人下、有人上,外在別人給你點(diǎn)評(píng)的標(biāo)簽、留下的烙印,都只是這趟車上的故事。只有個(gè)人成長(zhǎng)了、積累了、沉淀了,才有機(jī)會(huì)當(dāng)自己的司機(jī)。

可能某個(gè)年齡段的你還看不懂,但如果某天你不那么忙了,要思考思考自己的路、自己的腳步??纯催@些是不是你想要的,如果都是你想要的,為什么你看起來(lái)不開(kāi)心?

好!加油,走向你想成為的自己!

二、面試題

謝飛機(jī),小記!,中午吃飽了開(kāi)始發(fā)呆,怎么就學(xué)不來(lái)這些知識(shí)呢,它也不進(jìn)腦子!

「謝飛機(jī)」:喂,面試官大哥,我想問(wèn)個(gè)問(wèn)題。

「面試官」:什么?

「謝飛機(jī)」:就是這知識(shí)它不進(jìn)腦子呀!

「面試官」:這....

「謝飛機(jī)」:就是看了忘,忘了看的!

「面試官」:是不是沒(méi)有實(shí)踐?只是看了就覺(jué)得會(huì)了,收藏了就表示懂了?哪哪都不深入!?

「謝飛機(jī)」:好像是!那有什么辦法?

「面試官」:也沒(méi)有太好的辦法,學(xué)習(xí)本身就是一件枯燥的事情。減少碎片化的時(shí)間浪費(fèi),多用在系統(tǒng)化的學(xué)習(xí)上會(huì)更好一些。哪怕你寫寫博客記錄下,驗(yàn)證下也是好的。

三、先動(dòng)手驗(yàn)證垃圾回收

說(shuō)是垃圾回收,我不引用了它就回收了?什么時(shí)候回收的?咋回收的?

沒(méi)有看到實(shí)際的例子,往往就很難讓理科生接受這類知識(shí)。我自己也一樣,最好是讓我看得見(jiàn)。代碼是對(duì)數(shù)學(xué)邏輯的具體實(shí)現(xiàn),沒(méi)有實(shí)現(xiàn)過(guò)程只看答案是沒(méi)有意義的。

「測(cè)試代碼」

  1. public class ReferenceCountingGC { 
  2.  
  3.     public Object instance = null
  4.     private static final int _1MB = 1024 * 1024; 
  5.     /** 
  6.      * 這個(gè)成員屬性的唯一意義就是占點(diǎn)內(nèi)存, 以便能在GC日志中看清楚是否有回收過(guò) 
  7.      */ 
  8.     private byte[] bigSize = new byte[2 * _1MB]; 
  9.  
  10.     public static void main(String[] args) { 
  11.         testGC(); 
  12.     } 
  13.  
  14.     public static void testGC() { 
  15.         ReferenceCountingGC objA = new ReferenceCountingGC(); 
  16.         ReferenceCountingGC objB = new ReferenceCountingGC(); 
  17.         objA.instance = objB; 
  18.         objB.instance = objA; 
  19.         objA = null
  20.         objB = null
  21.         // 假設(shè)在這行發(fā)生GC, objA和objB是否能被回收? 
  22.         System.gc(); 
  23.     } 
  24.  

例子來(lái)自于《深入理解Java虛擬機(jī)》中引用計(jì)數(shù)算法章節(jié)。

例子要說(shuō)明的結(jié)果是,相互引用下卻已經(jīng)置為null的兩個(gè)對(duì)象,是否會(huì)被GC回收。如果只是按照引用計(jì)數(shù)器算法來(lái)看,那么這兩個(gè)對(duì)象的計(jì)數(shù)標(biāo)識(shí)不會(huì)為0,也就不能被回收。但到底有沒(méi)有被回收呢?

這里我們先采用 jvm 工具指令,jstat來(lái)監(jiān)控。因?yàn)楸O(jiān)控的過(guò)程需要我手敲代碼,比較耗時(shí),所以我們?cè)谡{(diào)用testGC()前,睡眠會(huì) Thread.sleep(55000);。啟動(dòng)代碼后執(zhí)行如下指令。

  1. E:\itstack\git\github.com\interview>jps -l 
  2. 10656 
  3. 88464 
  4. 38372 org.itstack.interview.ReferenceCountingGC 
  5. 26552 sun.tools.jps.Jps 
  6. 110056 org.jetbrains.jps.cmdline.Launcher 
  7.  
  8. E:\itstack\git\github.com\interview>jstat -gc 38372 2000 
  9.  S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT 
  10. 10752.0 10752.0  0.0    0.0   65536.0   6561.4   175104.0     0.0     4480.0 770.9  384.0   75.9       0    0.000   0      0.000    0.000 
  11. 10752.0 10752.0  0.0    0.0   65536.0   6561.4   175104.0     0.0     4480.0 770.9  384.0   75.9       0    0.000   0      0.000    0.000 
  12. 10752.0 10752.0  0.0    0.0   65536.0   6561.4   175104.0     0.0     4480.0 770.9  384.0   75.9       0    0.000   0      0.000    0.000 
  13. 10752.0 10752.0  0.0    0.0   65536.0   6561.4   175104.0     0.0     4480.0 770.9  384.0   75.9       0    0.000   0      0.000    0.000 
  14. 10752.0 10752.0  0.0    0.0   65536.0   6561.4   175104.0     0.0     4480.0 770.9  384.0   75.9       0    0.000   0      0.000    0.000 
  15. 10752.0 10752.0  0.0    0.0   65536.0   6561.4   175104.0     0.0     4480.0 770.9  384.0   75.9       0    0.000   0      0.000    0.000 
  16. 10752.0 10752.0  0.0    0.0   65536.0   6561.4   175104.0     0.0     4480.0 770.9  384.0   75.9       0    0.000   0      0.000    0.000 
  17. 10752.0 10752.0  0.0   1288.0 65536.0    0.0     175104.0     8.0     4864.0 3982.6 512.0  440.5       1    0.003   1      0.000    0.003 
  18. 10752.0 10752.0  0.0    0.0   65536.0   437.3    175104.0    1125.5   4864.0 3982.6 512.0  440.5       1    0.003   1      0.012    0.015 
  19. 10752.0 10752.0  0.0    0.0   65536.0   437.3    175104.0    1125.5   4864.0 3982.6 512.0  440.5     
  • S0C、S1C,第一個(gè)和第二個(gè)幸存區(qū)大小
  • S0U、S1U,第一個(gè)和第二個(gè)幸存區(qū)使用大小
  • EC、EU,伊甸園的大小和使用
  • OC、OU,老年代的大小和使用
  • MC、MU,方法區(qū)的大小和使用
  • CCSC、CCSU,壓縮類空間大小和使用
  • YGC、YGCT,年輕代垃圾回收次數(shù)和耗時(shí)
  • FGC、FGCT,老年代垃圾回收次數(shù)和耗時(shí)
  • GCT,垃圾回收總耗時(shí)

「注意」:觀察后面三行,S1U = 1288.0、GCT = 0.003,說(shuō)明已經(jīng)在執(zhí)行垃圾回收。

接下來(lái),我們?cè)贀Q種方式測(cè)試。在啟動(dòng)的程序中,加入GC打印參數(shù),觀察GC變化結(jié)果。

  1. -XX:+PrintGCDetails  打印每次gc的回收情況 程序運(yùn)行結(jié)束后打印堆空間內(nèi)存信息(包含內(nèi)存溢出的情況) 
  2. -XX:+PrintHeapAtGC  打印每次gc前后的內(nèi)存情況 
  3. -XX:+PrintGCTimeStamps 打印每次gc的間隔的時(shí)間戳 full gc為每次對(duì)新生代老年代以及整個(gè)空間做統(tǒng)一的回收 系統(tǒng)中應(yīng)該盡量避免 
  4. -XX:+TraceClassLoading  打印類加載情況 
  5. -XX:+PrintClassHistogram 打印每個(gè)類的實(shí)例的內(nèi)存占用情況 
  6. -Xloggc:/Users/xiaofuge/Desktop/logs/log.log  配合上面的使用將上面的日志打印到指定文件 
  7. -XX:HeapDumpOnOutOfMemoryError 發(fā)生內(nèi)存溢出將堆信息轉(zhuǎn)存起來(lái) 以便分析 

這回就可以把睡眠去掉了,并添加參數(shù) -XX:+PrintGCDetails,如下:

「測(cè)試結(jié)果」

  1. [GC (System.gc()) [PSYoungGen: 9346K->936K(76288K)] 9346K->944K(251392K), 0.0008518 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]  
  2. [Full GC (System.gc()) [PSYoungGen: 936K->0K(76288K)] [ParOldGen: 8K->764K(175104K)] 944K->764K(251392K), [Metaspace: 3405K->3405K(1056768K)], 0.0040034 secs] [Times: user=0.08 sys=0.00, real=0.00 secs]  
  3. Heap 
  4.  PSYoungGen      total 76288K, used 1966K [0x000000076b500000, 0x0000000770a00000, 0x00000007c0000000) 
  5.   eden space 65536K, 3% used [0x000000076b500000,0x000000076b6eb9e0,0x000000076f500000) 
  6.   from space 10752K, 0% used [0x000000076f500000,0x000000076f500000,0x000000076ff80000) 
  7.   to   space 10752K, 0% used [0x000000076ff80000,0x000000076ff80000,0x0000000770a00000) 
  8.  ParOldGen       total 175104K, used 764K [0x00000006c1e00000, 0x00000006cc900000, 0x000000076b500000) 
  9.   object space 175104K, 0% used [0x00000006c1e00000,0x00000006c1ebf100,0x00000006cc900000) 
  10.  Metaspace       used 3449K, capacity 4496K, committed 4864K, reserved 1056768K 
  11.   class space    used 376K, capacity 388K, committed 512K, reserved 1048576K 
  • 從運(yùn)行結(jié)果可以看出內(nèi)存回收日志,F(xiàn)ull GC 進(jìn)行了回收。
  • 也可以看出JVM并不是依賴引用計(jì)數(shù)器的方式,判斷對(duì)象是否存活。否則他們就不會(huì)被回收啦

「有了這個(gè)例子,我們?cè)俳又纯碕VM垃圾回收的知識(shí)框架!」

四、JVM 垃圾回收知識(shí)框架

垃圾收集(Garbage Collection,簡(jiǎn)稱GC),最早于1960年誕生于麻省理工學(xué)院的Lisp是第一門開(kāi)始使用內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)的語(yǔ)言。

垃圾收集器主要做的三件事:哪些內(nèi)存需要回收、什么時(shí)候回收、怎么回收。

而從垃圾收集器的誕生到現(xiàn)在有半個(gè)世紀(jì)的發(fā)展,現(xiàn)在的內(nèi)存動(dòng)態(tài)分配和內(nèi)存回收技術(shù)已經(jīng)非常成熟,一切看起來(lái)都進(jìn)入了“自動(dòng)化”。但在某些時(shí)候還是需要我們?nèi)ケO(jiān)測(cè)在高并發(fā)的場(chǎng)景下,是否有內(nèi)存溢出、泄漏、GC時(shí)間過(guò)程等問(wèn)題。所以在了解和知曉垃圾收集的相關(guān)知識(shí)對(duì)于高級(jí)程序員的成長(zhǎng)就非常重要。

垃圾收集器的核心知識(shí)項(xiàng)主要包括:判斷對(duì)象是否存活、垃圾收集算法、各類垃圾收集器以及垃圾回收過(guò)程。如下圖;

圖 27-1 垃圾收集器知識(shí)框架

原圖下載鏈接:http://book.bugstack.cn/#s/6jJp2icA

1. 判斷對(duì)象已死

1.1 引用計(jì)數(shù)器

  1. 為每一個(gè)對(duì)象添加一個(gè)引用計(jì)數(shù)器,統(tǒng)計(jì)指向該對(duì)象的引用次數(shù)。
  2. 當(dāng)一個(gè)對(duì)象有相應(yīng)的引用更新操作時(shí),則對(duì)目標(biāo)對(duì)象的引用計(jì)數(shù)器進(jìn)行增減。
  3. 一旦當(dāng)某個(gè)對(duì)象的引用計(jì)數(shù)器為0時(shí),則表示此對(duì)象已經(jīng)死亡,可以被垃圾回收。

從實(shí)現(xiàn)來(lái)看,引用計(jì)數(shù)器法(Reference Counting)雖然占用了一些額外的內(nèi)存空間來(lái)進(jìn)行計(jì)數(shù),但是它的實(shí)現(xiàn)方案簡(jiǎn)單,判斷效率高,是一個(gè)不錯(cuò)的算法。

也有一些比較出名的引用案例,比如:微軟COM(Component Object Model) 技術(shù)、使用ActionScript 3的FlashPlayer、 Python語(yǔ)言等。

「但是」,在主流的Java虛擬機(jī)中并沒(méi)有選用引用技術(shù)算法來(lái)管理內(nèi)存,主要是因?yàn)檫@個(gè)簡(jiǎn)單的計(jì)數(shù)方式在處理一些相互依賴、循環(huán)引用等就會(huì)非常復(fù)雜??赡軙?huì)存在不再使用但又不能回收的內(nèi)存,造成內(nèi)存泄漏

1.2 可達(dá)性分析法

Java、C#等主流語(yǔ)言的內(nèi)存管理子系統(tǒng),都是通過(guò)可達(dá)性分析(Reachability Analysis)算法來(lái)判定對(duì)象是否存活的。

它的算法思路是通過(guò)定義一系列稱為 GC Roots 根對(duì)象作為起始節(jié)點(diǎn)集,從這些節(jié)點(diǎn)出發(fā),窮舉該集合引用到的全部對(duì)象填充到該集合中(live set)。這個(gè)過(guò)程教過(guò)標(biāo)記,只標(biāo)記那些存活的對(duì)象 好,那么現(xiàn)在未被標(biāo)記的對(duì)象就是可以被回收的對(duì)象了。

GC Roots 包括;

  1. 全局性引用,對(duì)方法區(qū)的靜態(tài)對(duì)象、常量對(duì)象的引用
  2. 執(zhí)行上下文,對(duì) Java方法棧幀中的局部對(duì)象引用、對(duì) JNI handles 對(duì)象引用
  3. 已啟動(dòng)且未停止的 Java 線程

「兩大問(wèn)題」

誤報(bào):已死亡對(duì)象被標(biāo)記為存活,垃圾收集不到。多占用一會(huì)內(nèi)存,影響較小。

漏報(bào):引用的對(duì)象(正在使用的)沒(méi)有被標(biāo)記為存活,被垃圾回收了。那么直接導(dǎo)致的就是JVM奔潰。(STW可以確??蛇_(dá)性分析法的準(zhǔn)確性,避免漏報(bào))

2. 垃圾回收算法

2.1 標(biāo)記-清除算法(mark-sweep)

標(biāo)記-清除算法(mark-sweep)

  • 標(biāo)記無(wú)引用的死亡對(duì)象所占據(jù)的空閑內(nèi)存,并記錄到空閑列表中(free list)。
  • 當(dāng)需要?jiǎng)?chuàng)建新對(duì)象時(shí),內(nèi)存管理模塊會(huì)從 free list 中尋找空閑內(nèi)存,分配給新建的對(duì)象。
  • 這種清理方式其實(shí)非常簡(jiǎn)單高效,但是也有一個(gè)問(wèn)題內(nèi)存碎片化太嚴(yán)重了。
  • 「Java 虛擬機(jī)的堆中對(duì)象」,必須是連續(xù)分布的,所以極端的情況下可能即使總剩余內(nèi)存充足,但尋找連續(xù)內(nèi)存分配效率低,或者嚴(yán)重到無(wú)法分配內(nèi)存。重啟湯姆貓!
  • 在CMS中有此類算法的使用,GC暫停時(shí)間短,但存在算法缺陷。

2.2 標(biāo)記-復(fù)制算法(mark-copy)

標(biāo)記-復(fù)制算法(mark-copy)

  • 從圖上看這回做完垃圾清理后連續(xù)的內(nèi)存空間就大了。
  • 這種方式是把內(nèi)存區(qū)域分成兩份,分別用兩個(gè)指針 from 和 to 維護(hù),并且只使用 from 指針指向的內(nèi)存區(qū)域分配內(nèi)存。
  • 當(dāng)發(fā)生垃圾回收時(shí),則把存活對(duì)象復(fù)制到 to 指針指向的內(nèi)存區(qū)域,并交換 from 與 to 指針。
  • 它的好處很明顯,就是解決內(nèi)存碎片化問(wèn)題。但也帶來(lái)了其他問(wèn)題,堆空間浪費(fèi)了一半。

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

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

  • 1974年,Edward Lueders 提出了標(biāo)記-壓縮算法,標(biāo)記的過(guò)程和標(biāo)記清除算法一樣,但在后續(xù)對(duì)象清理步驟中,先把存活對(duì)象都向內(nèi)存空間一端移動(dòng),然后在清理掉其他內(nèi)存空間。
  • 這種算法能夠解決內(nèi)存碎片化問(wèn)題,但壓縮算法的性能開(kāi)銷也不小。

3. 垃圾回收器

3.1 新生代

1.Serial

算法:標(biāo)記-復(fù)制算法

說(shuō)明:簡(jiǎn)單高效的單核機(jī)器,Client模式下默認(rèn)新生代收集器;

2.Parallel ParNew

算法:標(biāo)記-復(fù)制算法

說(shuō)明:GC線程并行版本,在單CPU場(chǎng)景效果不突出。常用于Client模式下的JVM

3.Parallel Scavenge

算法:標(biāo)記-復(fù)制算法

說(shuō)明:目標(biāo)在于達(dá)到可控吞吐量(吞吐量=用戶代碼運(yùn)行時(shí)間/(用戶代碼運(yùn)行時(shí)間+垃圾回收時(shí)間));

3.2 老年代

1.Serial Old

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

說(shuō)明:性能一般,單線程版本。1.5之前與Parallel Scavenge配合使用;作為CMS的后備預(yù)案。

2.Parallel Old

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

說(shuō)明:GC多線程并行,為了替代Serial Old與Parallel Scavenge配合使用。

3.CMS

算法:標(biāo)記-清除算法

說(shuō)明:對(duì)CPU資源敏感、停頓時(shí)間長(zhǎng)。標(biāo)記-清除算法,會(huì)產(chǎn)生內(nèi)存碎片,可以通過(guò)參數(shù)開(kāi)啟碎片的合并整理。基本已被G1取代

3.3 G1

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

說(shuō)明:適用于多核大內(nèi)存機(jī)器、GC多線程并行執(zhí)行,低停頓、高回收效率。

五、總結(jié)

JVM 的關(guān)于自動(dòng)內(nèi)存管理的知識(shí)眾多,包括本文還沒(méi)提到的 HotSpot 實(shí)現(xiàn)算法細(xì)節(jié)的相關(guān)知識(shí),包括:安全節(jié)點(diǎn)、安全區(qū)域、卡表、寫屏障等。每一項(xiàng)內(nèi)容都值得深入學(xué)習(xí)。

如果不僅僅是為了面試背題,最好的方式是實(shí)踐驗(yàn)證學(xué)習(xí)。否則這類知識(shí)就像3分以下的過(guò)電影一樣,很難記住它的內(nèi)容。

整個(gè)的內(nèi)容也是小傅哥學(xué)習(xí)整理的一個(gè)過(guò)程,后續(xù)還會(huì)不斷的繼續(xù)深挖和分享。感興趣的小伙伴可以一起討論學(xué)習(xí)。

 

責(zé)任編輯:武曉燕 來(lái)源: bugstack蟲(chóng)洞棧
相關(guān)推薦

2021-03-15 08:11:40

JVM回收堆內(nèi)存

2011-05-11 09:01:29

面向?qū)ο蠹夹g(shù)函數(shù)式語(yǔ)言

2023-10-08 15:23:12

2023-12-07 12:21:04

GCJVM垃圾

2013-10-22 11:32:34

2022-09-15 10:44:42

SidecarIstioeBPFizer

2023-02-06 07:37:29

Java編程語(yǔ)言

2011-01-07 10:18:28

RSSWeb

2011-04-02 09:07:46

OOP

2012-01-11 11:07:04

JavaJVM

2012-02-20 10:12:09

Java

2014-01-06 09:36:53

IT部門BYODBYOA

2013-01-31 17:23:20

RIM黑莓BB10

2024-09-03 09:31:59

2020-02-29 15:18:10

DevOpsNoOps運(yùn)維

2020-02-19 11:35:21

iPhone越獲PP助手

2021-04-19 08:17:42

MesosKubernetesLinux

2010-01-12 09:43:15

Java已死

2024-09-20 17:08:44

JavaJVM開(kāi)發(fā)

2021-06-03 08:32:18

JVM調(diào)優(yōu)虛擬機(jī)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)