内存泄漏三问—vivo真题
说到性能优化,就不得不提下内存泄漏了,内存泄漏发生的原因以及解决办法你是否都已了解呢?看看今天的三问:
- 内存泄漏是什么,为什么会发生?
- 内存泄漏发生的情况有哪些?
- 该怎么发现和解决内存泄漏?
内存泄漏是什么,为什么会发生?
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放
,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
简单点说,手机给我们的应用提供了一定大小的堆内存
,在不断创建对象的过程中,也在不断的GC
(java的垃圾回收机制),所以内存正常情况下会保持一个平稳的值。但是出现内存泄漏就会导致某个实例,比如Activity的实例,应用被某个地方引用到了,不能正常释放,从而导致内存占用越来越大
,这就是内存泄漏。
内存泄漏发生的情况有哪些?
主要有四类情况
:
- 集合类泄漏
- 单例/静态变量造成的内存泄漏
- 匿名内部类/非静态内部类
- 资源未关闭造成的内存泄漏
1)集合类泄漏
集合类添加元素后,仍引用着集合元素对象,导致该集合中的元素对象无法被回收,从而导致内存泄露。
static List<Object> mList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Object obj = new Object();
mList.add(obj);
obj = null;
}
解决办法就是把集合也释放掉。
mList.clear();
mList = null;
2)单例/静态变量造成的内存泄漏
单例模式具有其静态特性
,它的生命周期等于应用程序的生命周期,正是因为这一点,往往很容易造成内存泄漏。
public class SingleInstance {
private static SingleInstance mInstance;
private Context mContext;
private SingleInstance(Context context){
this.mContext = context;
}
public static SingleInstance newInstance(Context context){
if(mInstance == null){
mInstance = new SingleInstance(context);
}
return sInstance;
}
}
比如这个单例模式,如果我们调用newInstance
方法时候把Activity的context
传进去,那么就是生命周期长的持有了生命周期短的引用,造成了内存泄漏。要修改的话把context改成context.getApplicationContext()
即可。
3)匿名内部类/非静态内部类
非静态内部类他会持有他外部类的强引用,所以就有可能导致非静态内部类的生命周期可能比外部类更长,容易造成内存泄漏,最常见的就是Handler
。
public class TestActivity extends Activity {
private TextView mText;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
mHandler. sendEmptyMessageDelayed(0, 100000);
}
怎么修改呢?改成静态内部类,然后弱引用方式修饰外部类
public class TestActivity extends Activity {
private TextView mText;
private MyHandler myHandler = new MyHandler(TestActivity.this);
private MyThread myThread = new MyThread();
private static class MyHandler extends Handler {
WeakReference<TestActivity> weakReference;
MyHandler(TestActivity testActivity) {
this.weakReference = new WeakReference<TestActivity>(testActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
weakReference.get().mText.setText("do someThing");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
myHandler.removeCallbacksAndMessages(null);
}
4)资源未关闭造成的内存泄漏
比如:
- 网络、文件等流忘记关闭
- 手动注册广播时,退出时忘记
unregisterReceiver()
- Service 执行完后忘记
stopSelf()
- EventBus 等观察者模式的框架忘记手动解除注册
该怎么发现和解决内存泄漏?
1、使用工具,比如Memory Profiler
,可以查看app的内存实时情况,捕获堆转储,就生成了一个内存快照,hprof
文件。通过查看文件,可以看到哪些类发生了内存泄漏。
2、使用库,比较出名的就是LeakCanary
,导入库,然后运行后,就可以发现app内的内存泄漏情况。
这里说下LeakCanary
的原理:
- 监听 首先通过
ActivityLifecycleCallbacks
和FragmentLifeCycleCallbacks
监听Activity和Fragment的生命周期。 - 判断 然后在销毁的生命周期中判断对象是否被回收。弱引用在定义的时候可以指定引用对象和一个
ReferenceQueue
,通过该弱引用是否被加入ReferenceQueue就可以判断该对象是否被回收。 - 分析 最后通过haha库来分析
hprof
文件,从而找出类之前的引用关系。
参考文章
https://juejin.im/post/6844903641032163336
- 使用 JavaScript 和 canvas 做精确的像素碰撞检测
- 锋利的JQuery —— JQuery性能优化
- memmove函数
- MFC常用的类详细介绍
- 锋利的JQuery —— Ajax
- 立即执行函数表达式(IIFE)
- windows程序设计-第四章 system2.c 新增滚动条功能
- 锋利的JQuery —— DOM操作
- strcpy函数和strncpy函数的区别
- windows程序设计-第四章 system1.c
- Vue.js 系列教程 2:组件,Props,Slots
- 《C++ primer》--第7章
- Vue.js 系列教程 1:渲染,指令,事件
- 【手把手教你Maven】构建过程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- AsyncTask记录
- spring cloud gateway跨域冲突功能的开发
- Spring同时集成JPA与Mybatis
- Qt音视频开发10-ffmpeg控制播放
- 拿好了!Linux 运维必备的 13 款实用工具!
- 自制CA证书设置ssl证书
- MySQL数据迁移TcaplusDB实践
- TKE之初识容器探测器
- 2.3.2 JDK动态代理 -《SSM深入解析与项目实战》
- mac设备安装nginx注意事项
- 《研发运营安全白皮书(2020年)》深度解读:全生命周期安全体系将是未来趋势
- 深度学习故障诊断:残差收缩网络 Residual Shrinkage Networks
- Java基于SSM的个人博客系统(源码 包含前后台)
- 聊聊RespServer
- Spring Boot系列之读取配置