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. leetcode 之Single Number(13)

    看见这题我的第一反应是用哈希来做,不过更简洁的做法是用异或来处理,只要是偶数个都为0(0和任意数异或仍为数本身). int singleNumber(int A[], int n) { ; ; i & ...

  2. 19:django 分页

    分页是网站中比较常见的应用,django提供了一些类帮助管理分页的数据,这些类都位于django.core.paginator.py文件里面 分页类 构造函数 class Paginator(obje ...

  3. linux命令(48):nl命令

    nl命令在linux系统中用来计算文件中行号.nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补齐 0 等 ...

  4. linux下不解包查看tar包文件内容

    为减少日志文件占用的空间,很多情况下我们会将日志文件以天或周为周期打包成tar.gz 包保存.虽然这样做有利空间充分利用,但当我们想查看压缩包内的内容时确很不方便.如果只是一个tar.gz文件,可以将 ...

  5. 配置文件中的mime-mapping元素(ofbiz/framework/catalina/config/mime-type)(

    mime-mapping元素将mime类型映射到扩展名. extension元素用来描述扩展名.mime-type元素则为MIME类型. <?xml version="1.0" ...

  6. Jmeter----读取excel表中的数据

    Jmeter 读取excel数据使用的方法是使用CSV Data Set Config参数化,之后使用BeanShell Sampler来读取excel表中的数据 第一步.查看所需的接口都要哪些字段和 ...

  7. 服务器环境从PHP5升级到PHP7

    #安装ppa sudo apt-get install python-software-properties software-properties-common sudo add-apt-repos ...

  8. Pycharm中的Django项目连接mysql数据库

    一.安装Pycharm和Django就不详细说了,自行百度 二.新建Django项目也不说了 三.配置Django连接到mysql 1.models.py写一个类,继承models.Model cla ...

  9. bzoj 3295 CDQ求动态逆序对

    #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk mak ...

  10. 多线程IO模型

    服务端编程,首要问题是选取IO模型.即如何处理大量连接,服务更多的客户端? 我们最早有2种解法,各有不足: 1.阻塞IO,每个连接都需要一个线程. 随着连接数增多,线程数剧增,系统开销太大. 2.非阻 ...