一、相关概念
1. 什么是内存泄漏
在堆上分配的对象无法被GC回收。对象无用;但对象可达。因为:该对象被一个生命周期更长的对象引用着。
2. 内存泄漏的后果:OOM
GC会定时扫描内存,发现不被引用的对象即可回收。如果Activity的引用被某个一直持有,那么返回-进入-退出这个Activity,会导致Activity创建多次,存在多个实例。当用户重复进行上述操作,导致多个Activity内存泄漏时,app运行一段时间堆内存超过系统规定的最大值heapSize,app崩溃。
heapSize:设备分配给app的最大堆内存,例如192M
maxHeapSize:当配置了android:largeHeap="true"时才有的最大堆内存,一般是heapSize的2-3倍,例如512M
ActivityManager manager=(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int heapSize=manager.getMemoryClass();
int maxHeapSize=manager.getLargeMemoryClass();
3. GC
GC为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等。
将对象的引用关系考虑为图的有向边,有向边从引用者指向被引用者
4. GCRoot
- 虚拟机栈(栈帧中的局部变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(一般说的native方法)引用的对象
5. 什么叫内存空间不足
Java4中引用级别(摘自_校招指南)内存不足:
6. 内存抖动
短时间内大量对象进出新生代(young generation),它伴随着频繁GC
- 产生原因
由大量对象在短时间内被配置而引起,我们要做的就是谨慎对待那些可能创建大量对象的情况。 - 如何避免
- 尽量避免在循环体内创建对象,应把对象创建移到循环体外
- 自定义view的
onDraw()
方法会被频繁地调用,所以在这里面不应该频繁地创建对象 - 当需要大量使用Bitmap时,试着把它们缓存在数组中实现复用
- 对于能够复用的对象,可以用对象池将它们缓存起来
来自 CoorChice 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/qq_31370269/article/details/52818277?utm_source=copy
二、常见内存泄漏场景
内存泄漏原因:生命周期长的对象引用了生命周期短的对象,导致生命周期短的对象无法被回收
1. 单例??传入Context还是单例吗
public class Singleton {
// 上下文
private Context context;
private static volatile Singleton instance=null;
private Singleton(Context context){
this.context=context;
}
public static Singleton getInstance(Context context){
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance = new Singleton(context);
}
}
}
return instance;
}
}
原因
上述代码是一个单例模式,若将Activity的上下文传入到这个单例类中,会导致Activity无法被回收,内存泄漏。
解决方法
- 将单例类中的context用弱引用修饰
public class Singleton {
// 上下文
private WeakReference<Activity> context;
private static volatile Singleton instance=null;
private Singleton(WeakReference<Activity> context){
this.context=context;
}
public static Singleton getInstance(Activity context){
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance = new Singleton(new WeakReference<Activity>(context));
}
}
}
return instance;
}
}
- 用Application的上下文代替Activity的上下文
instance = new Singleton(context.getApplicationContext());
2. 非静态内部类造成的内存泄漏
非静态内部类会持有外部类的引用,如果非静态内部类实例的生命周期比它的外部类的生命周期长,那么当销毁外部类时,会导致外部类无法被回收。
public class MainActivity extends AppCompatActivity{
private static Test test;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
if(test==null){
test=new Test();
}
}
private class Test{
}
}
原因
上述代码中
test
类变量引用了Test实例,其生命周期是整个应用的生命周期,非静态内部类持有外部类MainActivity
的引用
解决方法
- 将
test
变量设置为非static类型,使其生命周期和Activity一样长 - 将
Test
内部类设置为静态内部类
3. Handler、Runnable作为非静态内部类出现时
*/
public class MainActivity extends AppCompatActivity{
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
}
},10*1000);
finish();
}
}
原因
Handler、Runnable有定时功能,当它们作为非静态内部类时会持有外部类的引用。如果它们的内部有延时操作,在延迟操作还没有发生时,就销毁了外部类,那么外部类对象无法回收,内存泄漏。
解决方法:静态内部类+弱引用
- 将Handler、Runnable设置为静态内部类
- 在
onDestroy()
方法中移除Handler中的消息
public class MainActivity extends AppCompatActivity{
private Handler mHandler;
private Runnable mRunnable;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
mHandler=new TestHandler();
mRunnable=new TestRunnable();
mHandler.postDelayed(mRunnable,10*1000);
}
private static class TestHandler extends Handler{
}
private static class TestRunnable implements Runnable{
public void run(){
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mRunnable);
}
}
虽然上述代码解决了Handler作为静态内部类时不会持有外部类引用,但是有时我们会自己把Activity的上下文传到Handler里,这种情况下:可通过弱引用的方式引用Context,避免内存泄漏。
public class MainActivity extends AppCompatActivity{
private Handler mHandler;
private Runnable mRunnable;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
mHandler=new TestHandler(this);
mRunnable=new TestRunnable();
mHandler.postDelayed(mRunnable,10*1000);
finish();
}
private static class TestHandler extends Handler{
private WeakReference<Context> mContext;
// 通过构造函数传入上下文
public TestHandler(Context context) {
this.mContext = new WeakReference<Context>(context);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Context context=mContext.get();
if (context != null) {
// do something
}
}
}
private static class TestRunnable implements Runnable{
public void run(){
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mRunnable);
}
}
网友评论