java不同的引用类型,使用情况详细代码说明

之前关于这个,看了一些文章,但是总是觉得讲的不够清晰,自己都实践了很多次,才反映出来,其中暴漏出了自己很多不足,GC命令不熟悉,导致我测试了很久一直不触发,软引用的GC。本来代码里面留了很多不同的类型,不过感觉看起来还是太麻烦了,直接删除了
额外提示几句,static是方法区分配内存的。

设置JVM堆大小不要设置大了,我这里是12M,64位机器上应该都没问题,32位我没测试

用自己的话描述一下
强引用:非缓存,无法GC(指的是,生存周期之内,function没结束,结束了肯定缓存,我这里是main函数走到底)
软引用:缓存
弱引用:最差的缓存
虚引用:异步判断是否已经清缓存
这里请先了解一下几个JVM命令
JPS,直接输入,然后会输出所有的java线程

C:\Users\hasee>jps
10048 test
16080
23584 Jps
22756 Launcher
21944

我这里使用的是10048’
jstat 查看虚拟机堆内存使用情况,输入这个命令的原因是让你去看看GC的时间,解释

C:\Users\hasee>jstat -gc 10048
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
512.0  512.0   0.0   128.0   3072.0   549.0     8192.0     1041.4   4864.0 3355.6 512.0  364.6       5    0.003   2      0.012    0.016

具体解释如下—这个命令然后可以推出吞吐量和停顿时间的关系
1
2
3
1
2
3
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
更多命令解析,主要是这个命令的help不好用:
https://www.cnblogs.com/lizhonghua34/p/7307139.html 去这里
jmap:

C:\Users\hasee>jmap -heap 3764
Attaching to process ID 3764, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.66-b18

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 12582912 (12.0MB)
   NewSize                  = 4194304 (4.0MB)
   MaxNewSize               = 4194304 (4.0MB)
   OldSize                  = 8388608 (8.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 3145728 (3.0MB)
   used     = 496472 (0.47347259521484375MB)
   free     = 2649256 (2.5265274047851562MB)
   15.782419840494791% used
From Space:
   capacity = 524288 (0.5MB)
   used     = 0 (0.0MB)
   free     = 524288 (0.5MB)
   0.0% used
To Space:
   capacity = 524288 (0.5MB)
   used     = 0 (0.0MB)
   free     = 524288 (0.5MB)
   0.0% used
PS Old Generation
   capacity = 8388608 (8.0MB)
   used     = 8388448 (7.999847412109375MB)
   free     = 160 (1.52587890625E-4MB)
   99.99809265136719% used

1684 interned Strings occupying 152632 bytes.
然后设置下JVM的大小,不然设置个500M,测试起来很痛苦,这是IDEA的
这里写图片描述
这里写图片描述

public class test {

    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        //强引用,软引用,弱引用,虚引用
        TestObj strognerReferenceObj = new TestObj();
        TestObj softReferencObj = new TestObj();
        TestObj weakReferenceObj = new TestObj();
        TestObj padReferenceObj = new TestObj();
        //强引用不用管,就是正常使用,什么时候GC了?这个function,OVER掉
        SoftReference<TestObj> softReference = new SoftReference<TestObj>(softReferencObj);
        WeakReference<TestObj> weakReference = new WeakReference<TestObj>(weakReferenceObj);
        //虚引用,我觉得这个词是令人迷惑的,因为它其实是一种判断是否已经进入GC的形式,异步的
        ReferenceQueue queue = new ReferenceQueue();
        PhantomReference<TestObj> pf = new PhantomReference<TestObj>(padReferenceObj, queue);
        //很多文章里面写的是没有引用,太令人困惑了,并且他们给的例子总是需要=null,才会消除引用,所以可以另一个角度去看,这是一种异步回调判断的
        System.out.println("现在我们已经声明好了,所有引用类型");
        System.out.println("强:" + strognerReferenceObj + " 软:" + softReference.get() + " 弱:" + weakReference.get() + " 虚:" + pf.get());
        System.out.println("此时我们都是存在的,然后我们发起一次催促GC的请求,请注意,我只是催促,无法直接GC");
        System.gc();
        System.out.println("强:" + strognerReferenceObj + " 软:" + softReference.get() + " 弱:" + weakReference.get() + " 虚:" + pf.get());
        System.out.println("这里虚引用一直为Null,但是padReferenceStr其实还存在的,先看软引用和弱引用的区别");
        softReferencObj = null;
        weakReferenceObj = null;
        //请注意GC是催促的
        System.gc();
        while (weakReference.get() != null) {
            Thread.sleep(1000);
            System.out.println("正在等待GC。。。。");
        }
        System.out.println("强:" + strognerReferenceObj + " 软:" + softReference.get() + " 弱:" + weakReference.get() + " 虚:" + pf.get());
        Thread pfThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Object obj = queue.poll();
                    if (obj != null) {
                        try {
                            Field rereferent = Reference.class
                                    .getDeclaredField("referent");
                            rereferent.setAccessible(true);
                            Object result = rereferent.get(obj);
                            System.out.println("gc will collect:"
                                    + result.getClass() + "\t"
                                    + result.toString());
                        } catch (NoSuchFieldException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                        break;
                    }
                    System.out.println("还没进GC等1s");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        pfThread.start();
        padReferenceObj = null;
        //这里使用join是为了
        System.gc();
        pfThread.join();
        //软引用,最坑爹,先把虚引用写出来
        System.out.println("弱引用就没有了,那如何将软引用也给清楚掉了?需要等堆区满");
        LinkedList<TestObj> testObjs = new LinkedList<>();
        LinkedList<TestObj> extraObjs = new LinkedList<>();
        for (int i = 0; i < 50000; i++) {
            TestObj testObj = new TestObj();
            testObjs.add(testObj);
        }
        SoftReference<LinkedList<TestObj>> softReference1List = new SoftReference<LinkedList<TestObj>>(testObjs);
        testObjs = null;
        for (int i = 0; true; i++) {
            System.gc();
            //为什么我这里也加一个了,因为这个软GC,太坑爹了,找触发条件
            if (softReference.get() == null) {
                System.out.println("强:" + strognerReferenceObj + " 软:" + softReference.get() + " 弱:" + weakReference.get() + " 虚:" + pf.get());
                break;
            }
            if (softReference1List.get() == null) {
                System.out.println("软:" + softReference1List.get());
                break;
            }
            extraObjs.add(new TestObj());
        }
    }
}

class TestObj {
    int i;
    HashMap<String, String> map = null;

    public TestObj() {
        map = new HashMap<>();
        map.put("1", "1");
        //为了填满堆区,这里会GC掉
/*        for (int i = 0; i < 10; i++) {
            HashMap<StringBuilder, StringBuilder> hashMap = new HashMap<>();
        }*/
    }

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }
}

输出结果

现在我们已经声明好了,所有引用类型
强:algorithm.TestObj@238e0d81 软:algorithm.TestObj@31221be2 弱:algorithm.TestObj@377dca04 虚:null
此时我们都是存在的,然后我们发起一次催促GC的请求,请注意,我只是催促,无法直接GC
强:algorithm.TestObj@238e0d81 软:algorithm.TestObj@31221be2 弱:algorithm.TestObj@377dca04 虚:null
这里虚引用一直为Null,但是padReferenceStr其实还存在的,先看软引用和弱引用的区别
强:algorithm.TestObj@238e0d81 软:algorithm.TestObj@31221be2 弱:null 虚:null
gc will collect:class algorithm.TestObj algorithm.TestObj@41770d4b
弱引用就没有了,那如何将软引用也给清楚掉了?需要等堆区满
强:algorithm.TestObj@238e0d81 软:null 弱:null 虚:null

代码为主
代码值得说明的地方是:
我设置了2个软引用去测试,因为之前第一个我得测试方法有时候不出现结果,所以,我再写了一个软引用,这个软引用又很大,所以GC算法绝对会GC掉大的软引用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java 的虚引用(Phantom Reference)是一种比较特殊的引用类型,它的作用是帮助跟踪对象被垃圾回收的过程。虚引用并不会决定对象的生命周期,而是在对象被垃圾回收之前,虚引用会被加入到一个与之关联的引用队列,以便在对象被回收时得到通知。 以下是使用虚引用的示例代码: ``` // 创建一个对象 Object obj = new Object(); // 创建一个虚引用,与 obj 关联 ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); PhantomReference<Object> phantomRef = new PhantomReference<Object>(obj, queue); // 将 obj 设为 null,表示该对象可以被垃圾回收 obj = null; // 执行垃圾回收 System.gc(); // 检查引用队列,查看是否有虚引用被加入 Reference<? extends Object> ref = null; while ((ref = queue.poll()) != null) { if (ref == phantomRef) { // 如果虚引用被加入到队列,则表示 obj 已经被垃圾回收 // 可以在这里执行相关的清理操作 } } ``` 在上面的代码,创建了一个 Object 对象,并创建了一个与之关联的虚引用 phantomRef。随后,将 obj 设为 null,表示该对象可以被垃圾回收。执行 System.gc() 方法来触发垃圾回收,并通过轮询引用队列来检查虚引用是否被加入到队列。如果虚引用被加入到队列,则表示 obj 已经被垃圾回收,可以在这里执行相关的清理操作。 ### 回答2: Java的对象引用有强引用、软引用、弱引用和虚引用。虚引用是最弱的一种引用关系,它的存在几乎没有什么作用,也无法通过它获取到对象的实例。 虚引用主要是为了跟踪对象被垃圾回收器回收的活动。在Java,可以使用`java.lang.ref.PhantomReference`类来创建虚引用。下面是一个示例代码: ``` import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; public class PhantomReferenceExample { public static void main(String[] args) { Object obj = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); PhantomReference<Object> phantomReference = new PhantomReference<>(obj, referenceQueue); // 虚引用不会通过get()方法获取到对象的实例 System.out.println("获取到的对象实例:" + phantomReference.get()); // 输出null // 虚引用回收后会加入到引用队列 System.out.println("引用队列的引用个数:" + referenceQueue.poll()); // 输出:null obj = null; // 将对象设置为null,虚引用会在下一次垃圾回收时被回收 System.gc(); // 手动触发垃圾回收 // 等待一段时间,让垃圾回收线程完成回收操作 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Reference<?> reference = referenceQueue.poll(); if (reference != null) { System.out.println("引用队列的引用个数:" + reference); // 输出:java.lang.ref.PhantomReference@xxxxxx } } } ``` 在上述代码,首先创建一个虚引用`phantomReference`,它绑定了一个对象`obj`和一个引用队列`referenceQueue`。然后,在设置`obj`为`null`后,手动调用`System.gc()`触发垃圾回收。最后,通过`referenceQueue.poll()`方法,我们可以检查引用队列是否有已被回收的虚引用。 ### 回答3: 在Java,虚引用(Phantom Reference)是一种相对较少使用引用类型。与强引用、软引用和弱引用不同,虚引用的主要作用是帮助进行对象的垃圾回收跟踪。 虚引用在代码使用如下所示: ```java import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; public class PhantomReferenceExample { public static void main(String[] args) { Object obj = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); // 创建虚引用,并将对象与引用队列关联 PhantomReference<Object> phantomReference = new PhantomReference<>(obj, referenceQueue); // 通过虚引用获取对象 Object phantomObj = phantomReference.get(); // 返回null // 尝试回收对象 obj = null; System.gc(); // 检查引用队列 if (referenceQueue.poll() == phantomReference) { // 从引用队列获取引用并进行相应处理,如清理资源 // 这里可以根据需求进行自定义处理 System.out.println("虚引用指向的对象被回收"); } else { System.out.println("虚引用指向的对象未被回收"); } } } ``` 在上面的代码,首先创建了一个普通的对象obj,然后创建了一个ReferenceQueue对象referenceQueue,用来保存被回收的虚引用。接着创建了一个PhantomReference对象phantomReference,将obj与phantomReference关联起来。 在执行完obj = null和System.gc()之后,由于虚引用只能通过phantomReference.get()方法获取到null,obj对象会被回收,并加入到referenceQueue。我们可以通过referenceQueue.poll()方法来检查虚引用是否已被回收。 如果referenceQueue.poll()方法返回的是phantomReference,说明虚引用指向的对象被回收了。在实际应用,可以在这个位置进行相应的资源清理操作。否则,如果referenceQueue.poll()方法返回null,说明虚引用指向的对象未被回收。 虚引用的主要作用是在对象被垃圾回收之前,可以在代码做一些必要的清理工作。它在实际开发使用较少,主要是在一些特殊的场景下,如管理本地直接内存、监控对象是否被垃圾回收等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值