自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

簡析Android的垃圾回收與內(nèi)存泄露

移動(dòng)開發(fā) Android
Android系統(tǒng)是運(yùn)行在Java虛擬機(jī)上的,作為嵌入式設(shè)備,內(nèi)存往往非常有限,了解Android的垃圾回收機(jī)制,可以有效的防止內(nèi)存泄露問題或者OOM問題。本文作為入門文章,將淺顯的討論垃圾回收與內(nèi)存泄露的原理,不討論Dalvik虛擬機(jī)底層機(jī)制或者native層面的問題。

Android系統(tǒng)是運(yùn)行在Java虛擬機(jī)上的,作為嵌入式設(shè)備,內(nèi)存往往非常有限,了解Android的垃圾回收機(jī)制,可以有效的防止內(nèi)存泄露問題或者OOM問題。本文作為入門文章,將淺顯的討論垃圾回收與內(nèi)存泄露的原理,不討論Dalvik虛擬機(jī)底層機(jī)制或者native層面的問題。

1. 基礎(chǔ)

在分析垃圾回收前,我們要復(fù)習(xí)Java與離散數(shù)學(xué)的基礎(chǔ)。

  • 實(shí)例化:對(duì)象是類的一個(gè)實(shí)例,創(chuàng)建對(duì)象的過程也叫類的實(shí)例化。對(duì)象是以類為模板來創(chuàng)建的。比如Car car = new Car();,我們就創(chuàng)造了一個(gè)Car的實(shí)例(Create new class instance of Car)
  • 引用:某些對(duì)象的實(shí)例化需要其它的對(duì)象實(shí)例,比如ImageView的實(shí)例化就需要Context對(duì)象,就是表示ImageView對(duì)于Context持有引用(ImageView holds a reference to Context)。
  • 有向圖:在每條邊上都標(biāo)有有向線段的圖稱為有向圖,Java中的garbage collection采用有向圖的方式進(jìn)行內(nèi)存管理,箭頭的方向表示引用關(guān)系,比如 B ← A ,就是B中需要A,B引用A。
  • 可達(dá):有向圖D={S,R}中,對(duì)于Si,Sj 屬于S,如果從Si到Sj有任何一條通路存在,則可稱Si可達(dá)Sj。也就是說,當(dāng)B ← A中間箭頭斷了,就稱作不可達(dá),這時(shí)A就不可達(dá)B了。
  • Shallow heap 與 Retain heap 的對(duì)比
    • Shallow heap表示當(dāng)前對(duì)象所消耗的內(nèi)存
    • Retained heap表示當(dāng)前對(duì)象所消耗的內(nèi)存加上它引用的內(nèi)存總合 

 

 

 

Google I/O 2011: Memory management for Android Apps

上圖的橙色的Object是該有向圖的起點(diǎn),它的Shallow heap是100,而它的Retained heap是100 + 300 = 400。

2. 什么是垃圾回收

Java GC(Garbage Collection,垃圾收集,垃圾回收)機(jī)制,是Java與C++/C的主要區(qū)別之一,作為Java開發(fā)者,一般不需要專門編寫內(nèi)存回收和垃圾清理代碼,對(duì)內(nèi)存泄露和溢出的問題,也不需要像C程序員那樣戰(zhàn)戰(zhàn)兢兢。這是因?yàn)樵贘ava虛擬機(jī)中,存在自動(dòng)內(nèi)存管理和垃圾清掃機(jī)制。概括地說,該機(jī)制對(duì)虛擬機(jī)中的內(nèi)存進(jìn)行標(biāo)記,并確定哪些內(nèi)存需要回收,根據(jù)一定的回收策略,自動(dòng)的回收內(nèi)存,永不停息(Nerver Stop)的保證虛擬機(jī)中的內(nèi)存空間,防止出現(xiàn)內(nèi)存泄露和溢出問題。

3. 什么情況需要垃圾回收

對(duì)于GC來說,當(dāng)程序員創(chuàng)建對(duì)象時(shí),GC就開始監(jiān)控這個(gè)對(duì)象的地址、大小以及使用情況。通常GC采用有向圖的方式記錄并管理堆中的所有對(duì)象,通過這種方式確定哪些對(duì)象時(shí)“可達(dá)”,哪些對(duì)象時(shí)“不可達(dá)”。當(dāng)對(duì)象不可達(dá)的時(shí)候,即對(duì)象不再被引用的時(shí)候,就會(huì)被垃圾回收。

網(wǎng)上有很多文檔介紹可達(dá)的關(guān)系了,如圖,在第六行的時(shí)候,o2改變了指向,Obj2就不再引用main的了,即他它們是不可達(dá)的,Obj2就可能在下次的GC中被回收。 

 

 

 

developerWorks Java technology

4. 什么是內(nèi)存泄露

當(dāng)你不再需要某個(gè)實(shí)例后,但是這個(gè)對(duì)象卻仍然被引用,防止被垃圾回收(Prevent from being bargage collected)。這個(gè)情況就叫做內(nèi)存泄露(Memory Leak)。

下面將以How to Leak a Context: Handlers & Inner Classes這篇文章翻譯為例,介紹一個(gè)內(nèi)存泄露。

看如下的代碼

  1. public class SampleActivity extends Activity { 
  2.  
  3.   private final Handler mLeakyHandler = new Handler() {    @Override 
  4.     public void handleMessage(Message msg) {      // ...  
  5.     } 
  6.   } 
  7.  

當(dāng)你打完這個(gè)代碼后,IDE應(yīng)該就會(huì)提醒你

  1. In Android, Handler classes should be static or leaks might occur. 

它到底是如何泄露的呢?

  1. 當(dāng)你啟動(dòng)一個(gè)application時(shí),它會(huì)自動(dòng)在主線程創(chuàng)建一個(gè)Looper對(duì)象,用于處理Handler中的message。Looper實(shí)現(xiàn)了簡單的消息隊(duì)列,在循環(huán)中一個(gè)接一個(gè)的處理Message對(duì)象。大多數(shù)Application框架事件(比如Activity生命周期調(diào)用,按鈕點(diǎn)擊等)都在Message中,它們?cè)贚ooper的消息隊(duì)列中一個(gè)接一個(gè)的處理。注意Looper是存在于application整個(gè)生命周期中。
  2. 當(dāng)你新建了一個(gè)handler對(duì)象后,它會(huì)被分配給Looper的消息隊(duì)列。被發(fā)送到消息隊(duì)列的Message將保持對(duì)Handler的引用,因?yàn)楫?dāng)消息隊(duì)列處理到這個(gè)消息時(shí),需要使用[Handler#handleMessage(Message)](http://developer.android.com/reference/android/os/Handler.html#handleMessage(android.os.Message)這個(gè)方法。(也就是說,只要沒有處理到這個(gè)Message,Handler就一直在隊(duì)列中被引用)
  3. 在java中,非靜態(tài)的內(nèi)部Class與匿名Class對(duì)它們外部的Class有強(qiáng)引用。static inner class除外。 

 

 

 

引用關(guān)系

現(xiàn)在,我們嘗試運(yùn)行如下代碼

  1. public class SampleActivity extends Activity { 
  2.  
  3.   private final Handler mLeakyHandler = new Handler() {    @Override 
  4.     public void handleMessage(Message msg) {      // ... 
  5.     } 
  6.   }  @Override 
  7.   protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    // Post a message and delay its execution for 10 minutes. 
  8.     mLeakyHandler.postDelayed(new Runnable() {      @Override 
  9.       public void run() { /* ... */ } 
  10.     }, 1000 * 60 * 10);    // Go back to the previous Activity. 
  11.     finish(); 
  12.   } 
  13.  

這個(gè)程序很簡單,我們可以腦補(bǔ)一下,它應(yīng)該是啟動(dòng)了又瞬間關(guān)閉,但是事實(shí)真的是關(guān)閉了嗎?

稍有常識(shí)的人可以看出,它發(fā)送了一個(gè)Message,將在十分鐘后運(yùn)行,也就是說Message將被保持引用達(dá)到10分鐘,這就照成了至少10分鐘的內(nèi)存泄露。

最后正確的代碼如下

  1. ublic class SampleActivity extends Activity { 
  2.  
  3.   /** 
  4.    * Instances of static inner classes do not hold an implicit 
  5.    * reference to their outer class. 
  6.    */ 
  7.   private static class MyHandler extends Handler { 
  8.     private final WeakReference<SampleActivity> mActivity; 
  9.  
  10.     public MyHandler(SampleActivity activity) { 
  11.       mActivity = new WeakReference<SampleActivity>(activity); 
  12.     }    @Override 
  13.     public void handleMessage(Message msg) {      SampleActivity activity = mActivity.get();      if (activity != null) {        // ... 
  14.       } 
  15.     } 
  16.   }  private final MyHandler mHandler = new MyHandler(this);  /** 
  17.    * Instances of anonymous classes do not hold an implicit 
  18.    * reference to their outer class when they are "static"
  19.    */ 
  20.   private static final Runnable sRunnable = new Runnable() {      @Override 
  21.       public void run() { /* ... */ } 
  22.   };  @Override 
  23.   protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    // Post a message and delay its execution for 10 minutes. 
  24.     mHandler.postDelayed(sRunnable, 1000 * 60 * 10);    // Go back to the previous Activity. 
  25.     finish(); 
  26.   } 
  27.  

結(jié)論

  • GC是按照有向圖是否可達(dá)來判斷對(duì)象實(shí)例是否有用
  • 如果不在需要某個(gè)實(shí)例,卻仍然被引用,這個(gè)情況叫做內(nèi)存泄露
  • 匿名類/非靜態(tài)類內(nèi)部class會(huì)保持對(duì)它所在Activity的引用,使用時(shí)要注意它們的生命周期不能超過Activity,否則要用static inner class
  • 善于在Activy中的生命周期(比如onPause)中手動(dòng)控制其他類的生命周期

最后再補(bǔ)充一下iOS的情況,iOS在新版的OC與Swift中,已經(jīng)引入了新的內(nèi)存管理體系A(chǔ)RC(auto reference counting,引用自動(dòng)計(jì)數(shù)),C代碼在編譯時(shí),編譯器自動(dòng)適時(shí)的添加釋放內(nèi)存的代碼。

References

http://www.jianshu.com/p/22e73e80e950

http://www.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/

http://stackoverflow.com/a/70358/4016014

http://blog.csdn.net/luoshengyang/article/details/8852432

http://developer.android.com/training/articles/perf-tips.html

https://techblog.badoo.com/blog/2014/08/28/android-handler-memory-leaks/

責(zé)任編輯:龐桂玉 來源: 安卓開發(fā)精選
相關(guān)推薦

2014-12-19 11:07:40

Java

2011-08-15 16:28:06

Cocoa內(nèi)存管理

2018-07-17 08:58:57

垃圾回收內(nèi)存泄露方法

2023-12-19 21:52:51

Go垃圾回收開發(fā)

2017-08-04 10:53:30

回收算法JVM垃圾回收器

2009-09-02 09:23:26

.NET內(nèi)存管理機(jī)制

2010-06-02 13:00:43

Linux 內(nèi)存監(jiān)控

2024-02-04 09:18:00

Python內(nèi)存管理垃圾回收

2017-04-25 14:39:55

JVM內(nèi)存Java

2023-05-31 09:00:00

2022-03-21 11:33:11

JVM垃圾回收器垃圾回收算法

2010-01-27 10:52:15

Android垃圾回收

2009-12-09 17:28:34

PHP垃圾回收機(jī)制

2021-01-04 10:08:07

垃圾回收Java虛擬機(jī)

2022-01-20 10:34:49

JVM垃圾回收算法

2011-10-28 11:22:06

Android 4.0

2011-06-23 09:42:54

Android Mar應(yīng)用商店Android

2013-01-15 09:14:20

2023-03-10 12:28:16

2011-12-14 13:14:05

點(diǎn)贊
收藏

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