首页 资讯 社群 我的社区 搜索

WeakReference 关于java强弱对象引用的第一次接触

高大上
2019-05-19 13:28:57

早前看JVM底层原理时依稀记得有一张是关于对象引用的,当时看的一脸懵逼。今天写代码有幸接触到顺便记录下。

它的大意是java将引用对象分为4个类型:

【1.强引用】就是可以通过引用直接操作堆中的对象的情况。例如:StringBuffer str = new StringBuffer("HelloWord");这种类型的引用对象那个JVM在任何时候都不会被系统回收,即使JVM抛出OOM异常(OutOfMemorry堆空间溢出)也不会回收这部分空间。

【2.软引用】当内存紧张时JVM才会去考虑回收这篇空间。

【3.弱引用】只要GC触发就一定会被回收,即使堆空间很充足也会回收

【4.虚引用】虚引用是所有类型中最弱的一个。一个持有虚引用的对象,和没有引用几乎是一样的,随时可能被垃圾回收器回收。当试图通过虚引用的get()方法取得强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,销毁这个对象,将这个虚引用加入引用队列。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

这样归纳大家可能还是不明白这是啥玩意。简单来说,对象的引用在栈空间中,而实际的数据在堆空间中。当引用对象指向null或者不再被其他对象引用时,堆空间中的数据所占用的空间就因该被回收。但是释放时间是不固定的,哪些数据空间会被释放也是不固定的,都是GC说了算。GC也是人写的,设计GC回收的人就是按照上面的设计规则来实现的GC内存回收。并不是说所有对象不再被引用时的回收处理方式都是一致的。

那么知道这些对我们写代码有什么好处呢?GC又不是我们能左右的!!

是的,JVM帮我们做了很多细节封装。例如这里提到的内存的申请和释放。就java初级而言确实不用考虑这些,但是如果你想要走到更高的层次,这些知识还是又必要知道的。下面举个例子:

//handler 方法一
private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            System.out.println("handler 开始执行定时任务");
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    //在此执行定时操作
                    ProcessBlackList pb = new ProcessBlackList();
                    pb.check( manager,context,channel );
                    break;
                default:
                    break;
            }
        }
    };
//handler 方法二
public static class MHandler extends Handler {
        private WeakReference<NettyActivity> activity;

        public MHandler(NettyActivity activity) {
            this.activity = new WeakReference<NettyActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (activity == null || activity.get() == null) return;
            final NettyActivity nettyActivity = activity.get();
            switch (msg.what) {
                case NettyActivity.MSG_FROM_SERVER:
                    nettyActivity.notifyData( (SendMessage) msg.obj);
                    break;
            }
        }
    }

这是一段Android代码,关于handler使用的。方法一存在一个问题,就是当Activity被结束后MessageQueue并不会随之结束,如果这个消息队列中存在msg,则导致持有handler的引用,但是又由于Activity被结束了,msg无法被处理,从而导致永久持有handler对象,handler永久持有Activity对象,于是发生内存泄漏。

方法二就是优化办法:使用静态static,因为在java中所有非静态的对象都会持有当前类的强引用,而静态对象则只会持有当前类的弱引用。在配合weakReference限制activity为弱引用类型。这样activity被意外结束,当内存不足时 messageQueue中的内容依然会被回收。

如果你觉得还是不能理解,那就简单点:

1.少用全局大对象,如果有,尽量申明为static

2.局部变量中的大对象在使用完后记得置null

3.将JVM中关于垃圾分代回收的内容再看一遍。

用户评论