内存管理和内存泄漏

一、内存管理

1、Java内存分配策略

Java程序运行时的内存分配策略有三种。

静态方法区:主要存放静态static数据和常量。这块内存在程序编译时已经分配好,并且在程序整个运行期间都存在。

栈区:当方法被执行的时候,方法体内的局部变量都在栈上创建,并且在方法执行结束时这些局部变量所持有的内存都会自动释放。

堆区:又称动态内存分配,存储new创建的对象。这部分内存由java垃圾回收器gc负责管理。

2、Java内存管理

Java的内存管理就是对象的分配和释放问题。在java中,通过关键字new为每个对象申请内存空间,所有对象都在堆中分配空间。内存的释放由GC完成。GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请、引用和被引用等。释放对象的的原则是该对象不再被引用。

3、可达性分析

为了更好理解 GC 的工作原理,我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向被引对象。我们以 main 进程顶点开始。在这个有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象 与这个根顶点不可达,那么我们认为这个(这些)对象不再被引用,可以被 GC 回收。

图片

4、四种引用

强引用(不会被回收)、软引用(内存不足时)、弱引用(垃圾回收时)、虚引用(垃圾回收时)。

在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用。

软引用的使用:我们做图片Bitmap缓存,把Bitmap进行软引用,通过SoftReference的get方法获得bitmap对象,当内存不足时,bitmap将会被回收。

二、内存泄漏

1、什么是内存泄漏

在java中存在一些对象,这些对象有两个特点。一:这些对象是可达的(被引用)。二:这些对象是无用的。这些对象已经无效但被引用无法被回收,占据着内存,这就是内存泄漏。

2、常见的内存泄漏

1)资源对象未关闭

对于使用了BraodcastReceiver、EventBus、ContentObserver、File、游标 Cursor、Stream、Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

2)单例造成的内存泄漏

由于单例的静态特性使得其生命周期跟应用的生命周期一样长,所以如果使用不恰当的话,很容易造成内存泄漏。优先使用Application的Context,如需使用Activity的Context,可以在传入Context时使用弱引用进行封装,然后,在使用到的地方从弱引用中获取Context,如果获取不到,则直接return即可。

3)Handler 造成的内存泄漏

如果Handler发送了一个延迟消息,当Activity 被 finish() 的时候,延迟执行的 Message 还会继续存在于主线程中,Message会持有 Handler 引用,Handler是一个匿名内部类,Handler持有外部类Acitviy的引用,所以当Activity 销毁的时候,Activity不会被回收,从而造成内存泄漏。

解决方法:

使用静态内部类 + WeakReference ,那么handler存活期跟 Activity 的生命周期就无关了。同时通过弱引用的方式引入 Activity,避免直接将 Activity 作为 context 传进去。如果可以使用application的context,优先使用application的context。

public class SampleActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
//每次使用前注意判空。
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

// Go back to the previous Activity.
finish();
}
  @Override
protected void onDestroy() {
    super.onDestroy();
    //移除掉message,进行判空处理。
    mHandler.removeCallbacks(sRunnable)
}
}

4)非静态内部类创建静态实例造成的内存泄漏

非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。

public class MainActivity extends AppCompatActivity {
private static TestResource mResource = null;
  @Override  
  protected void onCreate(Bundle savedInstanceState) {
  s  uper.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if(mManager == null){
      mManager = new TestResource();
     }
      //...
  }
    class TestResource {
        //...
    }
}

**解决方案:**将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例。

一、内部类和静态内部类

在一个类的内部创建另一个类,叫做成员内部类。如果类用static修饰,就是静态内部类。

二、为什么静态内部类不会持有外部类的引用?

静态方法、静态变量、静态内部类,它们不依赖类特定的实例,被所有类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。所以静态内部类不会持有外部类的引用。

三、静态内部类什么时候被回收呢?

在程序中任何变量或者代码都是在编译时由系统自动分配内存来存储的,而所谓静态static就是指在编译后所分配的内存会一直存在,直到程序退出内存才会释放这个空间,也就是只要程序在运行,那么这块内存就会一直存在。

5)匿名内部类 创建异步线程

public class MainActivity extends Activity {
...
Runnable ref1 = new MyRunable();
Runnable ref2 = new Runnable() {
@Override
public void run() {
}
};
...
}

ref1和ref2的区别是,ref2使用了匿名内部类。我们来看看运行时这两个引用的内存:
图片

可以看到,ref1没什么特别的。但ref2这个匿名类的实现对象里面多了一个引用:

this$0这个引用指向MainActivity.this,也就是说当前的MainActivity实例会被ref2持有,如果将这个引用再传入一个异步线程,此线程和此Acitivity生命周期不一致的时候,就造成了Activity的泄露。

6)集合类只添加元素,而没有删除机制

集合类只添加元素,而没有删除机制。比如我们做缓存的时候,使用静态存储。那么缓存占据的空间随着APP的使用只增不减。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值