对GC垃圾收集的一点整理
2019獨角獸企業重金招聘Python工程師標準>>>
一、寫在前面
在正式進入故事之前,我們得先想幾個問題,
1.你是出于什么目的要去了解這件事情?
2.天天講垃圾收集,到底回收了哪些地方?
3.GC是通過什么方法回收的?回收了哪些東西?
?
在這里不會去寫具體的垃圾收集具體技術實現細節,首先能力有限,其次涉及的東西太多了,把我自己的理解和心得寫出來,希望有以上跟我一樣疑問的朋友看完之后,能有所幫助,望各路大神批評指正。
?
第一個疑問:出于什么目的要去了解這件事情?
我只能說一切源自生活,不存在丁點自己跟自己過不去的情況,有這空閑時間,寧愿多在微信上多撩幾個妹子更high, 因為在工作中太多的bug和時間耗在了排查內存溢出、內存泄露的情況,盡管這種情況不是經常出現,但出現這種情況的時候總是會耗費太多的精力去一遍遍的查找,分析,其次,第二個很大的原因是,當你的硬件配備已經最優、網絡配備已經最優、其他外在條件都有最優,但實際仍然無法滿足日益增大的系統性能要求時,很顯然,最根基的這塊垃圾收集就成了最后的問題瓶頸所在,治病除根。
第二個疑問:到底回收了哪些地方?
有了問題的引導,自然就引出了問題的解決辦法,我們把組成jvm運行時內存的幾個地方列出來分析分析,自然就有答案了。
?程序計數器:生命周期隨線程聲明周期而定,線程結束,生命結束,隨之結束之后GC自動回收。
?本地方法棧:生命周期隨線程聲明周期而定,線程結束,生命結束,隨之結束之后GC自動回收。
?棧:生命周期隨線程聲明周期而定,線程結束,生命結束,隨之結束之后GC自動回收。
? 以上三個片區可以理解為內存的分配和回收都有定數,不需要過多的去考慮垃圾回收問題。
————————————分隔符--------------------------------
?方法區:由程序運行期間動態分配空間,自然而然垃圾回收也是動態的,需要考慮垃圾回收問題(此片區可以配置為不回收,也就是所謂的“永久帶”,詳細細節參見下文描述)。
?堆:由程序運行期間動態分配空間,自然而然垃圾回收也是動態的,需要考慮垃圾回收問題。
由此可見,以上5個區都會存在GC回收情況,只是我們的關注重點是方法區、堆。
第三個疑問:通過什么方法回收的?回收了哪些東西?
通過第二個疑問我們知道,方法區和堆才是我們要關注的垃圾回收管理重點,那這兩者區別是啥呢?在jvm那些事中我們可以了解到,兩者都是被線程共享的內存區域部分,先來說說方法區, 方法區用于存放程序里面寫的常量、靜態變量、已被jvm加載的類信息,其實方法區可以看作是堆的邏輯部分區域,可參見java虛擬機規范中的定義,這塊區域會在GC分代收集中納入管理回收區域,只是實現方法區而已,那這樣一想,我們需要關注的重點就成了堆部分。下面看一下堆得組成部分及回收條件方法等。
這里只列出核心部分,便于記憶。
1.回收的前提條件:堆里面放著幾乎全部new出來的對象實例部分,在進行GC之前,要做的準備工作是GC得先判斷哪些對象還活著,哪些對象已死,活的對象保留,死的對象回收清除。所以,這里就引出了一個前置問題,用什么樣的方式方法去判定一個對象是活著的還是死的,就引入了一系列的判定算法,
1.1引用計數算法 (更細節的解釋說明可搜尋相關資料)
定義:為每個對象設置一個引用計數器,每引用一次該計數器加1,每解除引用一次該計數器減一,當引用計數器為0時,證明該對象沒有被引用,即可列入被回收的范圍之內。
優點:實現簡單,效率高。
缺點:存在循環引用無法釋放問題,導致某些情況無法回收,如? ?a=b,b=a。
1.2可達性分析算法(有些稱呼為根搜索算法)
定義:通過一些列被稱為GC ROOT的對象作為最頂層的父親節點,并將該節點作為起始點,開始往下搜索每一個分支節點的掛載對象,當一個對象,沒有任何途徑能連接到GC ROOT時,即可視為可以垃圾回收的對象,即使幾個子節點之間有互相引用但是無任何途徑連接到GC ROOT節點時,也視為可垃圾回收的對象。但是上述兩種情況,GC并不一定能回收他們,以為里面還有一個特例,對象逃逸,所以,這里即使出現沒有到GC ROOT的引用鏈條,也不會立即被回收,而是會做標記,后續會進行一些條件判定,核心部分對象的finalize方法部分。
?
堆得內存組成部分
為了便于更好的垃圾回收,將堆進行了分代,也可理解為對堆又進行了分區域劃分,劃分為了兩部分,年輕代、老年代。? 上面我們說方法區如果算是堆得邏輯部分的話,那也就是說方法區也可以看做是堆的一個邏輯組成部分,被很多人稱為永久代。
年輕代:由一個Eden和兩個Survivor區組成,E區和兩個S區大小空間占比默認為8:1:1,每次使用E區和一個S區,另一塊S區空閑,備用(復制算法),一般你new一個對象出來就是在年輕代上創建了,為什么是“一般”,因為如果當年輕代上沒有足夠的空間放你這個對象時,直接就把你扔到老年代上去了。這部分回垃圾回收時,采用復制算法,將E區和S區通過判定算法,將存活的對象一次性復制進備用的那塊S區,同時清空E區和被使用的那個S區,來實現垃圾回收。
老年代:存放大對象,在年輕代多次回收都沒有被回收的對象會進入老年代,老年代采用標記清除,標記整理的回收算法實現。
?
這里會引出一個問題,當進行垃圾回收時,你的應用到底應該處于一個什么樣的狀態呢?考慮這個問題是因為,進行著垃圾回收呢,如果應用一直在跑,那就有可能會出現有些對象明明在垃圾回收算法中已經標記為可回收了,還沒等回收剛好在這個過程中又重新被其他對象所引用了,此時進行垃圾回收就會報錯,所以,當垃圾回收事件進行時,出現了一件事,即stop the world世界停止了,意味著應用會停下來,并進入到GC執行過程中去,一旦該事件發生,除了GC自身所需的支撐線程外,其他線程都將停止工作,直到整個GC任務結束才繼續才能恢復,這就很可怕了,試想一下,你開發了一個應用,小到幾個人應用,大到每天面臨數以萬計、千萬級別的訪問使用量甚至更大,出現GC時,不能在用戶所能忍受的范圍之內處理好這件事情的話,將會是多么恐怖的一件事情,老板瘋了。因此你必須了解GC這塊的細節,并對其進行優化。
?
?
?
?
?
?
?
?
?
?
?
轉載于:https://my.oschina.net/tianshibuzuoai/blog/1610911
總結
以上是生活随笔為你收集整理的对GC垃圾收集的一点整理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 015PHP文件处理——文件处理floc
- 下一篇: 事务的实现