JVM GC 学习笔记(二)

JVM垃圾回收学习笔记

垃圾对象的判断和可触及性:

3种可触及性的状态

  • 可触及的:从根节点开始,可以到达这个对象。
  • 可复活的:对象的所有引用都被释放,但是对象有可能在finalize()函数中复活。
  • 不可触及的:对象的finalize()函数被调用,并且没有复活,那么就进入不可触及的状态,不可触及的对象不可能被复活,因为finalize()函数一个对象的生命周期中只会被调用一次。

以上三种状态中,只有在对象不可触及是才可以被安全的回收。

finalize()->对象的复活

对象通过finalize()函数来复活自己。如下例子:

public class FinalizeObjectDemo {

    public static FinalizeObjectDemo demo;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("FinalizeObjectDemo finalize called");
        demo = this;
    }

    @Override
    public String toString() {
        return "I am FinalizeObjectDemo";
    }

    public static void main(String[] args) throws InterruptedException {
        demo = new FinalizeObjectDemo();
        demo = null;
        System.gc();
        Thread.sleep(1000);
        if (demo == null) {
            System.out.println("demo is null");
        }else{
            System.out.println("demo is object");
        }
        System.out.println("second GC");
        demo = null;
        System.gc();
        Thread.sleep(1000);
        if (demo == null) {
            System.out.println("demo is null -");
        }else{
            System.out.println("demo is object -");
        }
    }
}

控制台输出:

FinalizeObjectDemo finalize called
demo is object
second GC
demo is null -

如上例子所示结果证明,finalize()导致引用外泄,对象复活,此时对象又变为可触及的状态。但是每个对象的生命周期只会调用一次finalize()函数。所以第二次GC时,则完成了GC的安全回收。

注意:不推荐使用finalize()函数来释放资源。

  • 由于finalize()函数有可能发生引用外泄,无意中复活对象。
  • 由于finalize()函数是被系统调用的,不能准确的把控调用。因此用于资源的释放不是一个明智的方案。推荐使用try-catch-finally语句中进行资源的释放。

引用和可触及性的强度

java中存在四个级别的引用:强引用,软引用,弱引用,虚引用

以上四种,除了强引用之外,其他三种在一定的条件下,均可以被回收。

强引用-不可回收的引用

强引用则是程序中常用的引用类型。都是可触及,可达的。

下面的例子则是一个强引用:

public class ReferenceDemo {

    public static void main(String[] args) {

        //强引用
        StringBuffer stringBuffer = new StringBuffer("hello world");

        //强引用
        StringBuffer stringBuffer1 = stringBuffer;

        System.out.println(stringBuffer == stringBuffer1);
    }
}
控制台输出结果:true

在这里插入图片描述

上述例子中,两个引用都属于强引用,强引用具有以下特点:

  • 强引用可以直接访问对象。
  • 强引用所指向的对象,在任何时候都不会被回收。
  • 强引用可能导致内存泄露。

软引用-可被回收的引用

软引用是比强引用弱一点的引用类型,当一个对象只持有软引用时,当堆空间不足时,则可以进行回收。

下面的例子演示了当Heap的内存空间不足时被回收启动参数设置:-Xmx10m

import java.lang.ref.ReferenceQueue;

public class SoftRefQueue {
    public static class User{
        private int id;

        private String name;

        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "[id:"+this.id+",name:"+this.name+"]";
        }
    }

    //创建一个软引用队列
    static ReferenceQueue<User> softQueue = null;

    public static class UserSoftReference extends java.lang.ref.SoftReference<User> {
        int uid;
        public UserSoftReference(User referent, ReferenceQueue<? super User> q) {
            super(referent, q);
            uid=referent.id;
        }
    }

    public static void main(String[] args) throws InterruptedException {

        Thread thread =new Thread(()->{
            while (true) {
                if (softQueue != null) {
                    UserSoftReference obj = null;
                    try {
                        obj = (UserSoftReference) softQueue.remove();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (obj != null) {
                        System.out.println("user id :"+obj.uid+" is delete");
                    }
                }
            }
        });
        thread.setDaemon(true);
        thread.start();

        User user = new User(1, "james");
        softQueue = new ReferenceQueue<User>();

        UserSoftReference userSoftReference = new UserSoftReference(user,softQueue);
        user = null;

        System.out.println(userSoftReference.get());

        System.gc();

        System.out.println("GC After");

        System.out.println(userSoftReference.get());

        System.out.println("try to create byte array and GC");

        byte[] bytes = new byte[1024 * 940 * 7];
        System.gc();

        System.out.println(userSoftReference.get());

        Thread.sleep(1000);
    }

}

控制台输出:
[id:1,name:james]
GC After
[id:1,name:james]
try to create byte array and GC
user id :1 is delete
null

以上的例子说明了,在堆内存资源正常的时候,软引用的对象不一定会被回收,但是在内存资源紧张的情况下,软引用的对象还是会被回收的。所以,软引用的对象时不会出现OOM的问题。

上述例子中出现的软引用队列:ReferenceQueue,当软引用对象的被回收时,软引用对象则被引入到Queue中去。本例子中开启一个守护线程轮询判断当前软引用对象是否被回收,当软引用被回收时加入这个队列中,则可以通过当前队列来跟踪对象的回收情况。

弱引用-发现即回收

弱引用是比软引用还弱的一种引用类型。在GC回收时,只要碰见弱引用则立即回收。弱引用在被GC回收时,回收的对象也会被加入到一个注册的引用队列中去,这点和软引用很相似。下面是代码举例:

public class WeakReferenceDemo {
    public static class User{
        private int id;

        private String name;

        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "[id:"+this.id+",name:"+this.name+"]";
        }
    }
    public static void main(String[] args) {
        User user = new SoftRefQueue.User(11, "James");

        WeakReference<User> userWeakReference = new WeakReference<User>(user);

        user = null;
        
        System.out.println(userWeakReference.get());

        System.gc();

        System.out.println(userWeakReference.get());
    }
}
控制台输出结果:
[id:11,name:James]
null

上述例子说明无论是否内存资源足够与否,弱引用碰见都是立即回收的。

虚引用-对象回收跟踪

**虚引用:**虚引用是所有引用类型中最弱的一个。虚引用随时都会被GC回收。虚引用使用时必须和队列一起使用。它的作用就在于跟踪垃圾回收过程。下面的例子说明虚引用:

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class TraceCanReliveObj {

    public static TraceCanReliveObj obj;
    static ReferenceQueue<TraceCanReliveObj> queue = null;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("obj is GC");
        obj= this;
    }

    @Override
    public String toString() {
        return "I am TraceCanReliveObj";
    }

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(()->{
            while (true) {
                if (queue != null) {
                    PhantomReference<TraceCanReliveObj> objs = null;
                    try {
                        objs = (PhantomReference<TraceCanReliveObj>) queue.remove();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (objs!= null) {
                        System.out.println("objs is delete by GC");
                    }
                }
            }

        });
        thread.setDaemon(true);
        thread.start();

        queue = new ReferenceQueue<TraceCanReliveObj>();

        obj = new TraceCanReliveObj();

        PhantomReference<TraceCanReliveObj> traceCanReliveObjPhantomReference =
                new PhantomReference<TraceCanReliveObj>(obj,queue);

        obj = null;
        System.gc();

        Thread.sleep(1000);
        if (obj ==  null){
            System.out.println("GC is finish");
        }else{
            System.out.println("GC is failure");
        }

        System.out.println(" the second  GC");

        obj = null;

        System.gc();

        Thread.sleep(1000);
        if (obj ==  null){
            System.out.println("GC is finish");
        }else{
            System.out.println("GC is failure");
        }

    }
}
控制台输出:
obj is GC
GC is failure
 the second  GC
objs is delete by GC
GC is finish

启动参数输入:-XX:+PrintGCDetails来查看GC日志GC的回收情况

上述例子说明:虚引用可以跟踪对象的回收,因此,可以将一些资源的释放操作放置在虚引用中执行和记录。

垃圾回收的停顿现象

*垃圾回收器的任务:*识别和回收垃圾对象进行内存清理。

*STW(Stop-The-World):*在GC时候的系统短暂性的停顿,为了保证系统中不会有新的垃圾出现。同时停顿保证了系统状态在某一瞬间的一致性。也有益于垃圾回收器更好的标记垃圾对象。

JVM GC 学习笔记(一)
JVM GC 学习笔记(三)

参考书籍:《实战Java虚拟机—JVM故障诊断与性能优化》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值