在Android 项目时的防止Memory leak 要注意的事项


一般来说,Java VM是会有Gargage Collect的。但是如果object在其他的object 有reference的话,那VM是不会做cleanup的。

常见的例子是在Activity 上发生。

因为在a) 由一个activity 跳到另一个activity 或是b)在screen rotation 时,Android 系统会新建一个新的Activity,而原先的Activity 会被释放,最后会被Gargage Collect.。但如果其他的Class 有用上这个Activity ,这个activity 就不会被Gargage Collect,因而引起memory leak 的问题。

例子

public class MyActivity extends Activity{

 

public void onCreate(Bundle icicle){

     super.onCreate(icicle);

     Utility.registerCallback(this);

    

}

 

}

 

public class Utility

{

private static Classback m_callback = new Callback();

 

public static void registerCallback(Context c){

     m_callback.registerCallback(c);

}

}

 

public class Callback {

private context m_context;

 

public void registerCallback(Context c){

     m_context=c;

}

 

public void unregisterCallback(Context c){

    if ( c == m_context ){

        m_context=null;

}

}

 

}

在以上的例子中,因为class Callback 中的m_context 把 MyActivity 抓着了。 所以在这个Activity完成后,Garage Collect是不可以释放MyActivity 的。

一个解决方案是在Activity离开前,释放在外面的reference。  

例子

public class MyActivity extends Activity{

 

public void onCreate(Bundle icicle){

     super.onCreate(icicle);

     Utility.registerCallback(this);

    

}

 

public void onDestroy()

{

    super.onDestroy();

    Utility.unregisterCallback(this);

}

 

}

 

public class Utility

{

private static Classback m_callback = new Callback();

 

public static void registerCallback(Context c){

     m_callback.registerCallback(c);

}

public static void unregisterCallback(Context c){

     m_callback.unregisterCallback(c);

}

 

}

 

public class Callback {

private context m_context;

 

public void registerCallback(Context c){

     m_context=c;

}

 

public void unregisterCallback(Context c){

    if ( c == m_context ){

        m_context=null;

}

}

}

其他的相关事项

1)在SDK中的DigitalClock Widget ,因为DigitalClock要注册一个observer,所以会用把载上DigitalClock widget的Activity作为那个observer。但在SDK的DigitalClock是有一个Bug的,因为在DigitalClock 的onDetachedFromWindow中是没有unregister 那个observer的。 解决方法是自己写一个DigitalClock。详情请看附上的代码(DigitalClockNew.java)。

2)在SDK中的Toast的function makeText 是要求传一个Context的,但在Toast的代码中,这个Context是会用做callback上。如果是传Activity作为那个Context的话,那会发生memory leak的问题。解决方法是传 Application Context,而不是ActivityContext。

如何查看Memory leak的问题

如果怀疑有memory leak的问题,可先执行那个apk在emulator上,然后在PC上执行adb shell,,然后执行ps,查看你的process 的pid。

在apk上执行有memory leak可疑的动作后,可以做下面的诊断

1.       执行 dumpsys meminfo <pid>

这个指令显示pid的memory资料。注意是 Activity的 数量,如果数量是不断上升,那就是有memory leak。 注意,因为在未执行Gargage Collect前,Activity是不会被释放的。可以在DDMS上执行手动的Gargage Collect。

2.       把process 的memory dump 出来,看memory leak的所在

先执行 chmod 777 /data/misc,然后执行 kill -10 <pid> ,在 /data/misc 上会看到那个pid 的memory leak,(如heap-dump-tm1265266619-pid1673.hprof )

用adb pull ( 如adb pull /data/misc/ heap-dump-tm1265266619-pid1673.hprof 1673.hprof )把这个档案拷回电脑上。

然后,用在PC上执行hprof-conv (如hprof-conv  1673.hprof  1673_a.hprof) 把那个拷回来的hprof转换成 Eclipse Memory Tool 可以支持的格式。

用Eclipse Memory Tool (http://www.eclipse.org/mat/), 打开转换了的memory dump。 在Eclipse Memory Tool 上,按OQL,输入 “select * from instanceof android.app.Activity” , 这个指令可以找所以在系统上是android.app.Activity 的 instance。(详见下图)

如果在object的旁带有“Unknown”的话,那object是可以被Gargage Collect的。要看其他的object是什么原因不可以被Gargage Collect 的话,可以在那个object上right-click,然后选Path to GC Roots, exclude weak/soft reference 。 (weak 和soft reference 都是可以被VM查到的,所以是可以Gargage Collect的。)

在Path to GC Roots 中,可以看到WidgetManagerHome 是因为Toast中的inner class TN 把 这个WidgetManagerHome抓住了。

相关内容