1,内存泄漏到本质是该释放的对象被持久化的对象引用了,造成持久化的常见情况有1,静态持久化 2,线程持久化

线程持久化

因为存活的线程是有dvk虚拟久直接持有,所以存活的线程都是持久化的

内存泄漏1:静态Activities(static Activities)

代码如下:
MainActivity.Java

public class MainActivity extends AppCompatActivity {
private static MainActivity activity;
TextView saButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
saButton = (TextView) findViewById(R.id.text);
saButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
setStaticActivity();
nextActivity();
}
});
}
void setStaticActivity() {
activity = this;
} void nextActivity(){
startActivity(new Intent(this,RegisterActivity.class));
SystemClock.sleep(1000);
finish();
} @Override
protected void onDestroy() {
super.onDestroy();
//使用LeakCanary观察是否有内存泄漏
MyApplication.getRefWatcher().watch(this);
}
}

LeakCanary检测出的内存泄漏:

为什么?
在上面代码中,我们声明了一个静态的Activity变量并且在TextView的OnClick事件里引用了当前正在运行的Activity实例,所以如果在activity的生命周期结束之前没有清除这个引用,则会引起内存泄漏。因为声明的activity是静态的,会常驻内存,如果该对象不清除,则垃圾回收器无法回收变量。

怎么解决?
最简单的方法是在onDestory方法中将静态变量activity置空,这样垃圾回收器就可以将静态变量回收。

@Override
protected void onDestroy() {
super.onDestroy();
activity = null;
//使用LeakCanary观察是否有内存泄漏
MyApplication.getRefWatcher().watch(this);
}

不使用静态activity,或给静态activity赋值时,考虑赋值的activity生命周期是不是全局的,或者在静态activity使用完后及时释放

内存泄漏2:静态View

代码如下:
MainActivity.java

    ...
private static View view;
TextView saButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
saButton = (TextView) findViewById(R.id.text);
saButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
setStaticView();
nextActivity();
}
});
}
void setStaticView() {
view = findViewById(R.id.sv_view);
}
...

LeakCanary检测到的内存泄漏

为什么?
上面代码看似没有问题,在Activity里声明一个静态变量view,然后初始化,当Activity生命周期结束了内存也释放了,但是LeakCanary却显示出现了内存泄漏,为什么?问题出在这里,View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity,所以当activity生命周期结束了,静态View没有清除掉,还持有activity的引用,因此内存泄漏了。

怎么解决?
在onDestroy方法里将静态变量置空。

@Override
protected void onDestroy() {
super.onDestroy();
view = null;
MyApplication.getRefWatcher().watch(this);
}
不使用静态view,或在activity关闭时将静态view赋值为null

内存泄漏3:静态内部类

代码如下:
MainActivity.java

private static Object inner;
void createInnerClass() {
class InnerClass {
}
inner = new InnerClass();
} View icButton = findViewById(R.id.ic_button);
icButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
createInnerClass();
nextActivity();
}
});
 

使用LeakCanary检测到的内存泄漏:

为什么?
非静态内部类会持有外部类的引用,在上面代码中内部类持有Activity的引用,因此inner会一直持有Activity,如果Activity生命周期结束没有清除这个引用,这样就发生了内存泄漏。

怎么解决?
因为非静态内部类隐式持有外部类的强引用,所以我们将内部类声明成静态的就可以了。

void createInnerClass() {
static class InnerClass {
}
inner = new InnerClass();
}
 

内存泄漏3:静态Drawable

当一个Drawable附加到一个 View上时,
View会将其作为一个callback设定到Drawable上。意味着Drawable拥有一个View的引用,上面说了view会有上下文的引用

内存泄漏4:静态集合中对象没清理造成的内存泄漏

我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。

内存泄漏5:单例导致内存泄露

单例模式在Android开发中会经常用到,但是如果使用不当就会导致内存泄露。因为单例的静态特性使得它的生命周期同应用的生命周期一样长,如果一个对象已经没有用处了,但是单例还持有它的引用,那么在整个应用程序的生命周期它都不能正常被回收,从而导致内存泄露。

public class AppSettings {

    private static AppSettings sInstance;
private Context mContext; private AppSettings(Context context) {
this.mContext = context;
} public static AppSettings getInstance(Context context) {
if (sInstance == null) {
sInstance = new AppSettings(context);
}
return sInstance;
}
}

像上面代码中这样的单例,如果我们在调用getInstance(Context context)方法的时候传入的context参数是ActivityService等上下文,就会导致内存泄露。

Activity为例,当我们启动一个Activity,并调用getInstance(Context context)方法去获取AppSettings的单例,传入Activity.this作为context,这样AppSettings类的单例sInstance就持有了Activity的引用,当我们退出Activity时,该Activity就没有用了,但是因为sIntance作为静态单例(在应用程序的整个生命周期中存在)会继续持有这个Activity的引用,导致这个Activity对象无法被回收释放,这就造成了内存泄露。

为了避免这样单例导致内存泄露,我们可以将context参数改为全局的上下文:

private AppSettings(Context context) {
this.mContext = context.getApplicationContext();
}

全局的上下文Application Context就是应用程序的上下文,和单例的生命周期一样长,这样就避免了内存泄漏。

单例模式对应应用程序的生命周期,所以我们在构造单例的时候尽量避免使用Activity的上下文,而是使用Application的上下文。

Android内存优化10 内存泄漏常见情况1 静态泄漏的更多相关文章

  1. Android内存优化12 内存泄漏常见情况3 注册泄漏

    android 中有很多注册和反注册,由于在注册后,上下文自身会被持久化的观察者列表所持有,如果不进行反注册,就会造成内存泄漏 内存泄漏1:Sensor Manager 代码如下: MainActiv ...

  2. Android内存优化11 内存泄漏常见情况2 内部类泄漏

    线程持久化 Java中的Thread有一个特点就是她们都是直接被GC Root所引用,也就是说Dalvik虚拟机对所有被激活状态的线程都是持有强引用,导致GC永远都无法回收掉这些线程对象,除非线程被手 ...

  3. Android内存优化13 内存泄漏常见情况4 资源泄漏

    资源未关闭或释放导致内存泄露 在使用IO.File流或者Sqlite.Cursor等资源时要及时关闭.这些资源在进行读写操作时通常都使用了缓冲,如果及时不关闭,这些缓冲对象就会一直被占用而得不到释放, ...

  4. Android 性能优化之内存泄漏检测以及内存优化(中)

    https://blog.csdn.net/self_study/article/details/66969064 上篇博客我们写到了 Java/Android 内存的分配以及相关 GC 的详细分析, ...

  5. 老李分享:Android性能优化之内存泄漏1

    老李分享:Android性能优化之内存泄漏   前言 对于内存泄漏,我想大家在开发中肯定都遇到过,只不过内存泄漏对我们来说并不是可见的,因为它是在堆中活动,而要想检测程序中是否有内存泄漏的产生,通常我 ...

  6. [Android 性能优化系列]内存之提升篇--应用应该怎样管理内存

    大家假设喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地 ...

  7. [Android 性能优化系列]内存之基础篇--Android怎样管理内存

    大家假设喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地 ...

  8. Android性能优化之内存篇

    下面是内存篇章的学习笔记,部分内容与前面的性能优化典范有重合,欢迎大家一起学习交流! 1)Memory, GC, and Performance 众所周知,与C/C++需要通过手动编码来申请以及释放内 ...

  9. Android内存优化9 内存检测工具3 MAT比Menmery Monitor更强大

    在Android性能优化第(一)篇---基本概念中讲了JAVA的四大引用,讲了一下GCRoot,第二篇Memory Monitor检测内存泄露仅仅说了Menmery Monitor的使用,这篇博客谈一 ...

随机推荐

  1. ASPxCheckBoxList控件获取selected项的text和value的方法

    设ASPxCheckBoxList的ClientInstanceName为list_ var needtext; for (var i = 0; i < list_.GetSelectedIte ...

  2. django “如何”系列9:三合一:利用遗留的数据库、输出csv和输出pdf

    如何集成遗留的数据库 django在适合开发新应用的同时,可以可以集成以前遗留的数据库,下面是如何集成一个已经存在的数据库的流程. 给定你的数据库的参数 你需要告诉django你的数据库连接参数以及数 ...

  3. 多路复用I/O模型poll() 模型 代码实现

    多路复用I/O模型poll() 模型 代码实现 poll()机制和select()机制是相似的,都是对多个描述符进行轮询的方式. 不同的是poll()没有描述符数目的限制. 是通过struct pol ...

  4. Mybatis学习—XML映射文件

    总结自 Mybatis官方中文文档 Mapper XML 文件 MyBatis 的真正强大在于它的映射语句,也是它的魔力所在.由于它的异常强大,映射器的 XML 文件就显得相对简单.如果拿它跟具有相同 ...

  5. 自定义wordCount程序、

    1.MyWordCount代码: package com.hadoop.mr; import java.io.IOException; import org.apache.hadoop.conf.Co ...

  6. 何時需要重启 OFBiz

    你在做如下更改時需要重新启動OFBiz服務器: - Java文件(記得要重新編譯) - 配置/.properties文件 - entitymodel或entitygroup XML定義文件 - 服務或 ...

  7. Linux安全之密钥登录

    我们一般使用 PuTTY 等 SSH 客户端来远程管理 Linux 服务器.但是,一般的密码方式登录,容易有密码被暴力破解的问题.所以,一般我们会将 SSH 的端口设置为默认的 22 以外的端口,或者 ...

  8. java入门概念梳理总结

    Java入门学习 简介 public class HelloWorld { public static void main(String []args) { System.out.println(&q ...

  9. JavaScript将最终获得正确的异步编程

    JavaScript将最终获得正确的异步编程 包括该提案异步 在ECMAScript中的功能已经达到第四阶段; 这意味着它将在2017年发布的标准.但是这对JavaScript开发者意味着什么? 有很 ...

  10. Vue结合原生js实现自定义组件自动生成

    就目前三大前端主流数据驱动框架(vue,ng,react)而言,均具有创建自定义组件的api,但都是必须先做到事先写好挂载点,这个挂载点可以是原有静态元素标签也可以是自定义模板:对于多种组件通过同一数 ...