前言
- 本文为制造一个”非静态内部类对外部类的引用持有”泄漏并对其结果进行观察作为学习使用,手段是制造泄漏,目的是了解泄漏产生的原因并未解决提供一种思路。
- 本文基于的思想是:2个Activity,其中一个Activity的内部类被外部引用挂住,导致该Acitvity无法正常回收。
- 本文只是对泄漏测试代码的一个讲解,并没有涉及到泄漏排查工具的使用,结合工具使用我会另开一片文章,https://blog.csdn.net/user11223344abc/article/details/80319915
Code
俩个Activity,一个SplashActivity,一个LeakActivity。操作路径是从Splash跳到LeakActivity。往返5次之后,手动Gc。
SplashActivity
package zj.com;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import zj.com.rxjava_operators.R;
public class SplashActivity extends AppCompatActivity {
private LeakActivity.TestResource testResource;
private LeakActivity.TestResource testResource2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
findViewById(R.id.leakgo).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(SplashActivity.this, LeakActivity.class));
if (null == testResource)
testResource = new LeakActivity().new TestResource(1, "张三");
if (null == testResource2)
testResource2 = new LeakActivity().new TestResource(2, "李四");
}
});
}
}
LeakActivity
package zj.com;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import zj.com.rxjava_operators.R;
public class LeakActivity extends AppCompatActivity {
private TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak);
mResource = new TestResource();
//...
}
class TestResource {
public TestResource() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
// //...
int age;
String name;
public TestResource(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
代码分析
- 可以看到,整个内存泄漏是由于LeakActivity的”非静态内部类”引起的。
- 你也注意到了,整个内部类TestResource,看起来挺别扭的,空参构造内还开个线程休息10秒,我为啥要这么写?
- 我测试时,遇见俩种情况使这个内存泄漏无法复现,(1)当我空参构造休眠1秒时,(2)当我的TestResource内部类里面啥都没写时。为什么?具体太细我也说不上,这个可能要从GC的算法判断和回收的机制说起,这里我先TODO,简单分析下,我觉得应该是JVM一个内部的机制(据说是指令优化?),使得GC回收监测到上述俩种情况就会将这类对象去回收掉,从而无法引起内存泄漏,但是值得注意的一点是,当我在空参构造内睡1秒而非是10秒时,GC这时会去回收这个对象,而10秒时候则不会去回收,我觉得应该是GC监测到了该内部类对象还有未执行完毕的任务时,就不会去回收。
- 再看SplashActivity,每次点击的时候,我都会创建俩个TestResource的对象,这俩个对象主要是让SplashActivity这个类从外部的因素去引用TestResource这个引用,从而引起内存泄漏。
总结
总结下,如果我们想要制造有内部类引起的内存泄漏:
- 那么这个内部类一定不能为空类
- 在没有外部影响的情况下,比如我在Splash内对Leak的内部类对象进行引用时,我们这时候就要让Leak.TestResource拥有足够的处理时间。
- 在有外部因素的影响下,这时候我们则可以不必考虑让TestResouce去长时间处理一个任务也可以复现内存泄漏。
- 本文Demo地址