内存泄漏:内存不在GC掌控之内了。当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而就导致,对象不能被回收。这种导致了本该被回收的对象不能被回收而停留在堆内存中,就产生了内存泄漏
四中引用:
StrongReference强引用:
回收时机:从不回收 使用:对象的一般保存 生命周期:JVM停止的时候才会终止
SoftReference软引用
回收时机:当内存不足的时候;使用:SoftReference结合- ReferenceQueue构造有效期短;生命周期:内存不足时终止
WeakReference,弱引用
回收时机:在垃圾回收的时候;使用:同软引用; 生命周期:GC后终止
PhatomReference 虚引用
回收时机:在垃圾回收的时候;使用:合ReferenceQueue来跟踪对象呗垃圾回收期回收的活动; 生命周期:GC后终止
开发时,为了防止内存溢出,处理一些比较占用内存大并且生命周期长的对象的时候,可以尽量使用软引用和弱引用。
软引用比LRU算法更加任性,回收量是比较大的,你无法控制回收哪些对象。
代码:
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memory_link);
//懒汉式单例
TestUtil instance = TestUtil.getInstance(this);
}
class TestUtil {
private static TestUtil instance;
private final Context mContext;
public TestUtil(Context context) {
this.mContext = context;
}
public static TestUtil getInstance(Context context) {
if (instance == null) {
instance = new TestUtil(context);
}
return instance;
}
}
上面代码是非常常见的懒汉式单例模式,安装apk运行后,在androidstudio中View-ToolWindwos-Profiler,打开内存分析工具,选择自己的应用。
此时可以看到CPU、MEMORY、ENERGY等的实时信息,我们分析内存相关,点击MEMORY,然后手机切换为横屏再切换为竖屏。
然后步骤1,点击dump按钮将hprof文件dump下来
在执行1、2步操作后生成dump信息,选中我们需要分析的app head 按ctrl + f 搜索自己应用的包名,可以看到应用内存在的对象,以及对象的个数,从第4、5步看出,app中有三个MemoryLinkActivity的对象,对象的depth值为0是可以被GC回收的,否则就不能回收,从第6步可以看出,当前对象被TestUtil持有,无法回收出现内存泄漏,这个对象就是最开始启动界面时,activity传给TestUtil的,一直被静态的TestUtil持有,无法释放,再看下一个对象
此对象没有被我们代码中的类持有,而是被ActivityThread持有,这个就是我们当前显示的界面对象,当前显示界面对象不会被回收,再看最后一个
这个对象既没有被我们代码中的类持有,又没有被ActivityThread持有,这个是横屏后的MemoryLinkActivity,此时正在等待被回收,它的depth值为0,是可以被回收的,下面验证下上文分析:
点击第1步的GC回收按钮,执行第2步再次dump出数据,再次观察dump数据,MemoryLinkActivity横屏的对象已经被GC掉,而被TestUtil持有的对象无法GC,存在泄漏,与之前的分析一致。
同样道理,我们将获取单例的代码注释掉,看看空activity的情况
// TestUtil instance = TestUtil.getInstance(this);
可以看到,横竖屏之后,有三个对象,两个可以回收,当前显示的不可以回收,GC后再观察:
除了当前对象,其余对象全部回收,完全不存在内存泄漏的问题。
至此,使用androidstudio profiler的基本操作完结,内存泄漏,gc就无法回收,一部分的内存在应用的这次生命周期中就不再可用,这样的泄漏多了就造成了OOM。所以,定位的时候,找到自己的包名,对比对象数据异常的类,看看哪些对象是GC之后还是没有释放的,再看看这些对象中哪些是合理的(如当前显示对象),其余的不合理的未释放的对象,就是泄漏的。
android profiler 其他一些字段含义:
您可以查看以下信息:
在类列表顶部,您可以使用左侧下拉列表在以下堆转储之间进行切换:
默认情况下,此堆中的对象列表按类名称排列。 您可以使用其他下拉列表在以下排列方式之间进行切换:
默认情况下,此列表按 Retained Size 列排序。 您可以点击任意列标题以更改列表的排序方式。
在 Instance View 中,每个实例都包含以下信息: