Java中的引用及作用分析

强引用

一般new的对象就是强引用,只有在没有引用时才会被垃圾回收掉。

Object a=new Object()

test:

public class M {
    /*
    * 此方法只有在被垃圾回收时调用
    */
    @Override
    protected void finalize() throws Throwable {
        System.out.println("M has called finalize");
    }
}

out:

public class ReferenceTst1 {
    public static void main(String[] args) throws IOException {
        M m=new M();
        m=null; //为空时M对象才被回收
        System.gc();
        System.in.read();//阻塞JVM进程
    }
}

public class StrengthReferenceTest {
    public static void main(String[] args) {
        M m=new M();
        M n=m;
        m=null;
        System.gc();
        //m为null,被回收
        System.out.println(m);
        //n指向M对象,不回收
        System.out.println(n);
    }
}

强引用对象回收:

public class Test1 {

    static class B{
        M m;
        public B(M m){
            this.m = m ;
        };
    }

    public static void main(String[] args) {
        //test1();
        testB();
    }

    /**
     * m,m1 指向同一内存地址(引用类型传递的是内存地址),此时M对象不会回收
     */
    public static void test1(){
        M m =new M();
        M m1 =m;
        m= null;
        System.gc();
    }

    /**
     * b对象回收,成员变量指向null, M对象无指向所以被回收
     */
    public static void testB(){
        B b =new B(new M());
        b=null;
        System.gc();
    }
}

软引用:SoftReference

堆内存空间不足时,才会被回收

/*
* 设置jvm启动参数,堆内存起始内存和最大内存为20M
* -Xms20M -Xmx20M
*/
public class SoftReferenceTest {
    public static void main(String[] args) {
        //创建大小为10M的软引用字节数组
        SoftReference<byte[]> bytes=new SoftReference<>(new byte[1024*1024*10]);
        System.out.println(bytes.get());
        //手动fullgc
        System.gc();
        //睡眠0.5s给回收时间
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //调用gc()后输出对象
        System.out.println(bytes.get());
        //重新申请堆内存15M的字节数组
        byte[] newBytes=new byte[1024*1024*12];
        //申请15M堆内存后输出对象
        System.out.println(bytes.get());
    }
}

如上代码,重新申请newBytes对象时,会自动回收bytes对象。软引用主要用作缓存。

弱引用:WeakReference

只要发生gc,就会跟随内存对象的回收而回收

public class WeakReferenceTest {

    public static void main(String[] args) {
        M m=new M();
        WeakReference<M> weakReference=new WeakReference<M>(m);
        m=null;
        //弱引用weakReference指向的是M对象,不回收,输出对象
        System.out.println(weakReference.get());
        System.gc();
        //m被回收,输出null
        System.out.println(m);
        //gc()后,弱引用weakReference对象被回收,输出null
        System.out.println(weakReference.get());

    }
}

弱引用的作用:

当两个或多个引用指向同一对象时(强引用),其中一个引用=null时,对象是不会回收的,因为存在其它强引用,若其他引用是弱引用,则会全部回收。

使用场景:ThreadLocal

 如上T1和Key都指向ThreadLocal对象。若key是强引用,即使t1=null,但key还是指向ThreadLocal对象,所以会有内存泄漏,而使用弱引用的话,key会随t1的回收而被回收。

但还是有内存泄漏的情况,如果key被回收后为null,则导致整个value再也无法被访问到,因此依然存在内存泄漏。

解决方案:每次使用

public class ThreadLocalTest {

    public static void main(String[] args) {
        String str="hello world";
        ThreadLocal<String> threadLocal=new ThreadLocal<>();
        threadLocal.set(str);
        System.out.println(threadLocal.get());
        str=null;
        threadLocal=null;
        System.gc();
        //此时ThreadLocalMap中key被回收为null,value还是指向 "hello world"内存空间。
        threadLocal.remove();
        //调用remove()后,才真正清空value.
    }
}

不理解ThreadLocal内存泄漏,请再看下面这个例子:


public class RTest {

    /* 经典用法----通过一个类去继承WeakReference,然后调用父类的构造方法,把某个对象设置成弱引用  */
    static class R extends WeakReference<M> {
        Object obj;
        /**
         * 前面一个是弱引用,后面一个是强引用
         * @param referent
         * @param obj
         */
        public R(M referent, Object obj) {
            super(referent);
            this.obj = obj;
        }
    }

    public static void main(String[] args) {
        test();
        // test()出栈,对象被回收,调用垃圾回收m1 对象此时被回收
        System.gc();
    }

    public static void test(){
        M m =new M();
        M m1= new M();
        //注意传参:m 是弱引用, m1 是强引用
        R r =new R(m,m1);
        m =null;
        m1 =null;
        //调用垃圾回收,m被回收,m1不会回收
        System.gc();
        System.out.println(r.get());
        /*r= null;
        System.gc();*/
    }
}

虚引用:PhantomReference

Phantom:虚幻的

随时被垃圾回收器回收,并且虚引用访问不到被引用的对象。

/*
* 设置jvm启动参数,堆内存起始内存和最大内存为20M
* -Xms20M -Xmx20M
*/
public class PhantomReferenceTest {
     //接受消息的队列
     static final ReferenceQueue referenceQueue=new ReferenceQueue();
     static final List LIST=new LinkedList();

    public static void main(String[] args) throws IOException {
        //虚引用对象
        PhantomReference<M> phantomReference=new PhantomReference(new M(),referenceQueue);
        new Thread(()->{
            while (true) {
                LIST.add(new byte[1024 * 1024]);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(phantomReference.get()); //虚引用访问不了,输出null,但是对象并未回收
            }
        }).start();

        new Thread(()->{
            while (true){
                Reference poll=referenceQueue.poll();
                if (poll!=null){
                    System.out.println("...虚引用对象被JVM回收了:"+poll);
                }
            }
        }).start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

作用:管理堆外内存

NIO里有个DirectByteBuffer 也叫堆外内存。当监测到虚引用被垃圾回收器回收后,可以作出相应的处理去回收堆外内存。(netty)

堆外内存怎么回收?

sun.misc.Unsafe

Unsafe unsafe=Unsafe.getUnsafe();

allocateMemory(long var1) 手动申请内存

freeMemory(long var1) 手动释放内存

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值