Android記憶體最佳化1 瞭解java GC 記憶體回收機制1

來源:互聯網
上載者:User

標籤:產生   回收機制   本地   常量   還需   頻繁   第一步   處理   style   

開篇廢話

如果我們想要進行記憶體最佳化的工作,還是需要瞭解一下,但這一塊的知識屬於純理論的,有可能看起來會有點枯燥,我盡量把這一篇的內容按照一定的邏輯來走一遍。首先,我們為什麼要學習記憶體回收的機制,我大概歸納了一下幾點:

1.方便我們理解什麼樣的對象,什麼時候,會被系統回收掉2.有助於我們後面的記憶體最佳化3.瞭解這一塊的知識也能提升自己的知識廣度,和同事一起裝逼的時候有話題4.如果有面試的需求的話,瞭解這一塊,也能從容面對考官,對於記憶體回收能夠說出個一二

好了,廢話不多說了,我大概按以下這個邏輯來一個一個講述:

1.什麼是記憶體回收(GC)2.記憶體回收機制對於我們來說有什麼好處?又有什麼缺點?3.記憶體回收它是如何工作的?
技術詳情1.什麼是記憶體回收(GC)

記憶體回收或GC(Garbage Collection),是一種自動的儲存管理機制,它是Java語言的一大特性,方便了我們這些程式員編碼,把記憶體釋放工作的壓力都轉讓到了系統,故而是以消耗系統效能為代價的。C++編碼的時候,我們 需要自己實現解構函式來進行記憶體釋放,很麻煩,而且非常容易遺漏而最終導致程式崩掉。所以Java語言就引入了自動記憶體管理的機制,也就是記憶體回收機制,針對的主要的記憶體的堆地區,關於記憶體的分配機制,請查看我的上一篇Android效能調優篇之探索JVM記憶體配置

2.記憶體回收機制對於我們來說有什麼好處?又會帶來哪些坑?

以下我列舉一下系統自動記憶體回收給我們帶來的一些好處:

1.讓作為程式員的我們專註於邏輯實現,提高了我們的編碼效率2.能夠保護程式的完整性,記憶體回收是Java語言的安全性原則的一個重要部分

但是隨之的,也會到來一些缺點:

1.系統層面負責記憶體回收,明顯會加大系統資源的開銷,從而影響程式的效能2.記憶體回收機制也存在不完備性,並不能百分百保證回收所有的垃圾記憶體
3.記憶體回收它是如何工作的?

其實,GC是主要的一個流程是:先根據一定的演算法判定某個對象是否存活,然後把判定是垃圾的對象進行回收。詳細點說的話,GC的工作流程分以下幾個步驟:

1.可回收對象的判定2.通過某些演算法回收垃圾記憶體

下面一個一個進行講述

1.可回收對象的判定

我們的GC需要把某個對象回收掉,肯定是需要判斷它到底是不是垃圾,是不是需要被回收,因此,就需要對每一個對象進行可回收判定。

目前,市面上存在有兩種演算法來判定一個對象是否是垃圾

1.引用計數演算法

這種演算法的工作原理是:

1.首先給每一個對象都添加一個引用計數器2.當程式的某一個地方引用了此對象,這個計數器的值就加13.當引用失效的時候(例如超過了範圍),這個計數器就減14.當某一個對象的計數器的值是0的時候,則判定這個對象不可能被使用

這種演算法對於系統來說比較簡單,高效,記憶體回收行程運行較快,不需要長時間中斷我們的程式的執行,但是缺點是很難處理循環參考,這就導致相互引用的對象都無法被回收:

循環參考

我記得OC(Objective-C)中的垃圾判定就是用的引用計數方法,引用了一個第三方變數來打破這個平衡,但OC也沒有很好的解決這個問題,而是更多的依靠我們這些開發人員來處理。

2.GC Root可達性分析演算法

這個演算法的工作原理是:

1.以稱作“GC Root”的對象作為起點向下搜尋2.每走過一個對象,就產生一條引用鏈3.從根開始到搜尋完,產生了一棵引用樹,那些到GC Root不可達的對象就是可以回收的

這個方法明顯就解決了循環參考的問題,不過這個演算法還是稍微有點複雜的,以下是GC Root可達性演算法的一個圖解:

比較

我們程式中能夠被用來當做GC Root對象的有:

1.虛擬機器棧(棧幀中的本地變數表)中引用的對象2.方法區中靜態屬性引用的對象3.方法區中常量引用的對象4.本地方法棧中JNI引用的對象

以下拿一個圖來進行引用計數演算法與可達性分析演算法的比較:

比較

文字說明:

1.若使用引用計數演算法判定,但圖中的C和D對象存在相互引用,導致計數器不為0,無法回收掉2.若使用可達性分析演算法,C和D對象到GC Roots 不可達,則能夠回收

關於對象可回收的判定,我們還需要注意的是,當系統開始啟動GC的時候,為了保證引用鏈的狀態不變,就需要停止該進程中所有的程式(Stop The World),我們Android中的現象就是UI卡頓了,但一般這樣的卡頓時間是非常短的,當然,要是頻繁的產生GC,那就另當別論了。

還有值得注意的是,不可達對象也並非立即就被回收了,還需要經過兩次的標記過程後才被會被真正回收:

1.如果對象與GC Root沒有串連的引用鏈,就會被第一次標記,隨後判定該對象是否有必要執行finalize()方法2.如果有必要執行finalize()方法,則這個對象就會被放到F-Queue的隊列中,稍後由虛擬機器建立低優先順序的Finalizer線程去執行,但並不承諾等待它運行結束(對象類中能夠重寫finalize()方法進行自救,但系統最多隻能執行一次)3.如果沒必要執行finalize()方法,則第二次標記
2.通過某些演算法回收垃圾記憶體

以上內容講述了系統如何去判定某一個對象是否是垃圾,是否應該被回收。接著,當判定了某一個對象為垃圾對象後,系統就要開始進行回收了,那麼系統的記憶體回收演算法以下幾種:

1.標記清除演算法(Mark-Sweep)2.複製演算法(Copying)3.標記整理演算法(Mark-Compact)4.分代回收演算法

以下進行一一講述

1.標記清除演算法(Mark-Sweep)

顧名思義,這個演算法是先進行標記,然後進行清除,也正是這個演算法的兩個階段:標記階段和清除階段,以解:

標記清除

可以看出:

這個演算法的優點是易於理解,容易實現,只需要將特定地址的空間進行處理。但缺點也比較明顯,把整個記憶體地區弄得非常不完整,形成了很多片段化的記憶體,對於分配大記憶體的對象時,無法申請足夠的空間,從而更多次的觸發GC
2.複製演算法(Copying)

複製演算法,是對標記清除演算法而導致記憶體片段化的一個解決方案,演算法原理如下:

複製演算法
1.,複製演算法將記憶體平均分成兩個地區A (上)和 B(下)2.將A中的存活的那些對象複製到B地區中3.然後將A地區的所有對象都清除,這樣A地區就是一個完整的記憶體塊了,也就避免了記憶體片段化了

但是這個演算法也有明顯的缺點,那就是不管A地區或B地區有多少個存活對象,都需要將整塊記憶體分成兩個地區,意味著能夠真正使用的記憶體變成了一半。

3.標記整理演算法(Mark-Compact)

標記整理演算法是對於標記清楚的一個最佳化,工作原理是:

標記整理
1.,第一步也需要進行存活對象的一個標記,這一步與標記清除演算法一模一樣2.將存活的對象向一端移動,例中是往左上方那一端進行移動3.然後把另一端的記憶體進行清理

也可以看出,這個演算法也能避免記憶體片段化的問題,但是效率確實不怎麼樣,畢竟相較於複製演算法, 多了一步效率同樣比較低的標記過程,而與標記清除演算法相比,多了一步記憶體整理(往一端移動)的過程,效率上明顯就更低了。

畢竟世界是公平的,任何演算法都有兩面性,我們開發人員只能具體情況具體分析,使用最適合的演算法。因此標記整理演算法可以看出,這個演算法適合存活對象多的,回收對象少的情況。

4.分代回收演算法

鑒於以上三種演算法都存在自己的缺陷,然後大神們就提出了根據不同對象的不同特性,使用不同的演算法進行處理,所以嚴格來講並不是一個新的演算法,而是屬於一種演算法整合方案,我們知道:

1.複製演算法適用於存活對象少,回收對象多的情況2.標記整理演算法適用於存活對象多,回收對象少的情況

這兩種演算法剛好互補,因此只要將這兩個演算法作用於不同特性的對象,就完美了。。

那麼我們就應該知道,哪個地區的對象是什麼樣的特性,根據我的上一篇的記憶體配置模型,堆記憶體中的新生代有很多的垃圾需要回收,老年代有很少的垃圾需要回收,那麼剛好能夠根據這個特點使用不同的演算法進行回收,具體使用的方式為:

1.對於新生代地區採用複製演算法,因為新生代中每次記憶體回收都要回收大部分對象,那麼也就意味著複製次數比較少,因此採用複製演算法更高效2.而老年代地區的特點是每次回收都只能回收很少的對象,一般使用的是標記整理或者標記清除演算法

通過以上的方式,使得GC的整個過程達到了最高效的狀態。

這裡需要注意的是分代回收演算法的中的複製演算法的使用。

之前說的複製演算法是將記憶體均分為二,但是在分代回收中,並不是這樣,而是根據Eden:Survivor A:Survivor B= 8:1:1,具體的過程是(簡化版):

1.新建立一個對象,預設是分在Eden地區,當Eden地區記憶體不夠的時候,會觸發一次Minor GC(新生代回收),這次回收將存活的對象放到Survivor A地區,然後新的對象繼續放在Eden地區2.再建立一個新的對象,要是Eden地區又不夠了,再次觸發Minor GC,這個時候會把Eden地區的存活對象以及Survivor A地區的存活的對象移動到Survivor B地區,然後清空Eden地區以及Survivor A地區3.如果繼續有新的對象建立,不斷觸發Minor GC,有些對象就會不斷在Survivor A地區以及Survivor B地區來回移動,但移動次數超過15次後,這些對象就會被移動到老年代地區4.如果新的對象在Minor GC後還是放不下,就直接放到老年代5.如果Survivor地區放不下該對象,這直接放到老年代6.如果老年代也滿了,就會觸發一次Full GC(major gc)
乾貨總結

通過以上的講述,我們瞭解了什麼是GC,它的優缺點,它是如何工作的,整過過程下來,腦子裡也算有了一個GC的機率模型在了。

本文參考了以下部落格:

理解Android Java記憶體回收機制

JAVA記憶體回收機制

掌握好GC策略和原理,對於我們編碼來說能夠避免一些不必要的記憶體泄露,我們使用Java語言進行開發,不要一味的去追求各種牛逼的架構或者酷炫的業務實現,有的時候,還是需要我們沉下心來,好好瞭解一下底層系統的一些機制,個人覺得還是很有必要的。

Android記憶體最佳化1 瞭解java GC 記憶體回收機制1

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.