内存泄露

什么是内存泄露?

内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,
造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
Android发生内存泄漏的常见情况
静态变量
静态变量的生命周期和应用的生命周期一样长。如果静态变量持有某个Activity的context,则会引发对应Activity无法释放,导致内存泄漏。如果持有application的context,就没有问题(以下例子是指Activity销毁时没有释放的情况)

常见的有:

  • 单例模式:内部实现是静态变量和方法
  • 静态的View:view默认持有Activity的context
  • 静态Activity

package com.example.testmemoryleak;

import android.content.Context;

import android.os.Handler;

import android.os.Message;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import com.squareup.leakcanary.LeakCanary;

public class MainActivity extends AppCompatActivity {

private Button btn;

private Button btn1;

private static Context StaticVarible;

private Handler mHandler;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

btn = (Button)findViewById(R.id.button);

btn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

MainActivity.this.finish();

}

});

StaticVarible = this;

LeakCanary.install(getApplication());

}

private static class NoLeakHandler extends Handler{

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

}

}

}

结果如下:
在这里插入图片描述

再ActivityDestory时讲静态变量置空即可

@Override

protected void onDestroy() {

StaticVarible = null;

super.onDestroy();

}

匿名内部类或者非静态内部类
常见的包括

Handler,AsyncTask,TimerTask等,一般在处理多线程任务的时候

非静态的内部类和匿名内部类都会隐式地持有其外部类的引用(否则怎么访问外部类的非静态成员呢?),静态的内部类不会持有外部类的引用

Java中的类可以是static吗?答案是可以。在java中我们可以有静态实例变量、静态方法、静态块。类也可以是静态的。
java允许我们在一个类里面定义静态类。比如内部类(nested class)。把nested class封闭起来的类叫外部类。在java中,我们不能用static修饰顶级类(top level class)。只有内部类可以为static。
静态内部类和非静态内部类之间到底有什么不同呢?下面是两者间主要的不同。
(1)内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。
(2)非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。
(3)一个非静态内部类不能脱离外部类实体被创建,一个非静态内部类可以访问外部类的数据和方法,因为他就在外部类里面。

那么修改方法 1.静态内部类 2.销毁前及时处理非静态内部类

在这里插入代码片
package com.example.testmemoryleak;

import android.content.Context;

import android.os.Handler;

import android.os.Message;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import com.squareup.leakcanary.LeakCanary;

public class MainActivity extends AppCompatActivity {

private Button btn;

private Button btn1;

private static Context StaticVarible;

private Handler mHandler;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

btn = (Button)findViewById(R.id.button);

btn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

MainActivity.this.finish();

}

});

btn1 = (Button)findViewById(R.id.button1);

btn1.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

mHandler = new Handler(){

@Override

public void handleMessage(Message msg){

super.handleMessage(msg);

}

};

Message message = Message.obtain();

message.what = 1;

mHandler.sendMessageDelayed(message,5*60*1000);

/*

mHandler = new NoLeakHandler();

Message message = Message.obtain();

message.what = 1;

mHandler.sendMessageDelayed(message,5*60*1000);

Log.i("weijuncheng", String.valueOf(mHandler.hasMessages(1)));

*/

}

});

StaticVarible = this;

LeakCanary.install(getApplication());

}

@Override

protected void onDestroy() {

//mHandler.removeCallbacksAndMessages(null);

StaticVarible = null;

super.onDestroy();

}

private static class NoLeakHandler extends Handler{

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

}

}

}

在这里插入图片描述
如上,Activity销毁时非静态内部类mHandler中还有未处理的消息,造成无法释放其引用的Activity对象

修改方案:

  1. 使用静态内部类(必要时结合WeakReference,弱引用方式)

作者:weiinter105
链接:https://www.jianshu.com/p/8ade6b469cd7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


private static class NoLeakHandler extends Handler{

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

}

}


mHandler = new NoLeakHandler();

Message message = Message.obtain();

message.what = 1;

mHandler.sendMessageDelayed(message,5*60*1000);

Log.i("weijuncheng", String.valueOf(mHandler.hasMessages(1)));

静态内部类延伸
静态内部类也可以用来实现单例,保证线程安全,效率高;其主要原理为:Java中静态内部类可以访问其外部类的静态成员属性,同时,1.静态内部类只有当被调用的时候才开始首次被加载(懒加载),2.利用了classloader的机制来保证初始化instance时只有一个线程(多个线程就不是静态类了),所以也是线程安全的,同时没有性能损耗(加synchronized同步锁)

与饿汉方式不同的地方在,饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化(相当于一个在static语句中初始化,相当于在中初始化;另一个相当于在静态内部类的中进行单例的初始化)

代码示例如下

public class Singleton {

    private Singleton() {}

    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE; //lazy-loading
    }
}

销毁前及时处理非静态内部类


@Override

protected void onDestroy() {

mHandler.removeCallbacksAndMessages(null);

super.onDestroy();

}

监听器
当使用Activity的context注册监听,不再需要监听时没有取消注册

资源对象
资源对象未关闭:BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,使用后未关闭会导致内存泄漏。因为资源性对象往往都用了一些缓冲,缓冲不仅存在于 java虚拟机内,还存在于java虚拟机外。如果仅仅是把它的引用置null,而不关闭它们,也会造成内存泄漏

容器
容器中的对象没有清理:集合一般占用内存较大,不及时关闭会导致内存紧张(不会导致内存泄漏,而会导致可用内存大大减少)

WebView
检测、分析内存泄漏的工具
MemoryMonitor:随时间变化,内存占用的变化情况

MAT:输入HRPOF文件,输出分析结果

  • Histogram:查看不同类型对象及其大小
  • DominateTree:对象占用内存及其引用关系

LeakCanary:实时监测内存泄漏的库

如何配置LeakCanary开源库


apply plugin: 'com.android.application'

android {

compileSdkVersion 28

defaultConfig {

applicationId "com.example.testmemoryleak"

minSdkVersion 15

targetSdkVersion 28

versionCode 1

versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {

debug{

}

release {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

}

}

}

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'com.android.support:appcompat-v7:28.0.0'

implementation 'com.android.support.constraint:constraint-layout:1.1.3'

testImplementation 'junit:junit:4.12'

androidTestImplementation 'com.android.support.test:runner:1.0.2'

androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'

releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'

// Optional, if you use support library fragments:

debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'

}

如何避免内存泄漏
不要在匿名内部类中进行异步操作

将非静态内部类转为静态内部类 + WeakReference(弱引用)的方式

在 Activity 回调 onDestroy 时或者 onStop 时

  • 移除消息队列 MessageQueue 中的消息
  • 静态变量置null
  • 停止异步任务
  • 取消注册

使用Context时,尽量使用Application 的 Context

尽量避免使用static 成员变量。另外可以考虑lazy初始化

为webView开启另外一个进程,通过AIDL与主线程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放

及时关闭资源。Bitmap 使用后调用recycle()方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值