之前看了一些java的垃圾回收機制的文章,感覺全網都有,但是寫的參差不齊,有的寫的超好,有的總覺得怪怪的,所以還是自己整理一番!(如有雷同,純屬抄襲。。。)
哎,“寒冬”啊,大家日子都不好過!
概念:
垃圾回收機制就是當你寫你的java代碼的時候,基本不需要考慮內存資源的佔用和釋放,爲什麼說基本不需要考慮呢,因爲java有垃圾回收機制來幫你做這件事情,但是爲什麼又沒有說完全不考慮呢?因爲如果你的代碼寫的稀爛,OOM是很常見的操作!所以垃圾回收機制,就是幫你清理掉已經沒有用的內存佔用(比如一些用完不會再用的對象),但是我們需要減少GC
垃圾回收算法:
那市面上都有哪些常見的垃圾回收算法呢?
1、標記-清除法(Mark-Sweep)
很好理解,兩個步驟,先找到存活的對象(標記),接着當執行垃圾回收的時候,清除其他對象。是最原始最基礎最簡單的算法
缺點:對象被刪除之後,沒有整理過堆(heap),結果產生大量不連續的內存碎片,當我們創建一個大對象(大對象需要連續的一整塊內存,比如一個很長的集合,Map等),就會導致提前觸發下一次的GC
這張圖來自:https://www.cnblogs.com/jiangtunan/p/11025521.html(作者:將圖南)
2、複製算法(Copying)
怕內存被碎片化,所以提出新的解決方案。
直接把內存砍成兩半(叫A和B),在使用A的時候,B是空的,當觸發垃圾回收的時候,把A中存活的對象複製到B中,然後清空A,接着新來的對象,就放入B中,以此類推循環往復。
缺點:太明顯了好嗎,我買了16G的內存,結果最大隻能用8G????還有另外一個缺陷,對象存活率比較高,那麼就會出現一大部分數據反覆的複製過來複制過去
這張圖來自:https://blog.csdn.net/yrwan95/article/details/82829186(作者:蜜汁蛋總)
3、標記-整理算法(Mark-Compact)
有一些垃圾回收器,使用了標記-清除法(Mark-Sweep)之外,還會週期性的調用標記-整理算法,用來對堆的碎片進行定期的整理!
先找到存活的對象(標記)
然後將存活的對象按照一定的規則挪到同一側(或者同一邊),
更新存活對象的應用的指針值(因爲實際堆中的對象換了位置,所以棧中對象的引用指針需要更新)
然後清空邊界以外的所有對象
這其中還涉及到按照什麼規則排?
(具體我就不展開了,大家有興趣可以看如下pdf,應該是需要翻牆的)
缺點:這種算法其實還蠻不錯的了
缺點就是需要更多的空間來存儲引用(不過java本身new對象的時候就是引用+堆中的對象)
通常比標記-清除和複製算法更慢,需要更多的堆的傳遞(因爲把堆上的對象做了一定規則的排序)
pdf中的原文:
Has some space overheads incurred bystoring forwarding addresses.
Usually has a slower throughput than marksweep or copying GC, as it requires more passes over the heap.
這張圖來自:https://blog.csdn.net/wuzhiwei549/article/details/80563134(作者:1Vincent)
4、分代收集算法(Generational Collection)
分代要滿足一個假設:絕大多數的對象生命週期都非常短暫,甚至有些用一次之後就不用了!
核心:把內存分爲兩部分,兩部分使用不同的垃圾收集機制
比如分爲新生代和老年代,新生代每次都有大比例的對象死去,那就可以用複製算法,老年代,對象存活率高,就可以用標記整理,java就是這麼做的!
java中垃圾回收簡述:
java垃圾回收就是分代收集算法的一種延伸,每個內存空間又有自己獨有的處理方式
1、對象先創建到Eden中
2、當新對象創建的時候,Eden區已經放不下了,就會發生一次Minor GC,這是小規模的GC手段,目的就是清理Eden空間,將Eden中存活的對象挪到Survivor的From中,等到下一次Minor GC的時候,會將Eden中存活的+SurvivorFrom中存活的全部挪到To中,下一次再反過來挪到From中,存活的對象每挪動一次,年齡就會+1。以此類推
啥時候年輕代的對象會進入老年代呢?
內存分配與回收策略(對象晉升老年代):https://www.jianshu.com/p/0772dc04e1d9(作者:是一動不動的friend)
3、當有對象進入到老年代,如果老年代滿了就會清理老年代,這是一個非常耗時的GC(我們需要儘量避免觸發這類的GC)
爲什麼我這裏沒有寫Full GC呢,因爲具體我也不確定到底觸發的是哪種,我估計是Full GC,大家如果想深入研究可以看如下的鏈接
- Major GC 是清理老年代。
- Full GC 是清理整個堆空間—包括年輕代和老年代。
Minor GC、Major GC和Full GC之間的區別:https://www.cnblogs.com/tuhooo/p/7508503.html(作者也是轉載的)
如下圖是java的heap中內存分配的比例 :
java官網相關參數的設置和默認值:https://www.oracle.com/java/technologies/vmoptions-jsp.html
JVM內存配置詳解:https://www.cnblogs.com/qmfsun/p/5396710.html(作者:Agoly)
我們是可以通過不同的參數來設置這些值的比例的,具體還是要根據自己項目來判定
每個個接口每秒會接受多少請求,每個請求會創建多大的對象,每秒會有多少對象進入到Eden,這些對象是否是臨時對象等等一系列問題
最後的最後,說一個問題,看到很多文章說垃圾回收算法是找到要需要刪除的對象,然後刪除那些對象,我想這樣說是錯誤的,應該是找到存活的對象,那麼通過什麼手段尋找呢?
叫做可達性分析算法(GC Roots),大概來說就是從一些可以作爲“根”(root)的節點往下尋找存活的對象,並且標記這些(存活)對象(具體也不是我三言兩語就能說明白的)。
大家有興趣可以去研究下
JAVA垃圾回收-可達性分析算法:https://blog.csdn.net/luzhensmart/article/details/81431212(翻譯作者:這瓜保熟麼)
菜雞一隻,正在努力提升自己,本文如果有哪些寫的不對的,歡迎大家指出,我一定及時更改!
(接下來想看看java不同垃圾回收的區別,比如什麼G1,什麼CMS),下次再見拜拜~