【Java原理探索】夯实你对Java对象引用的认识

简介: 【Java原理探索】夯实你对Java对象引用的认识

前提概要


不知道我的新主题,大家喜不喜欢,我把它叫做风车主题,哈哈,有过喜欢的小伙伴们,可以大家一起讨论,做一些更好的主题给大家,接下来给大家介绍一下今天的主题内容:引用的分类,针对于GC来讲,引用方式是一种非常重要的知识点,这可以让我们更加深入的理解GC和JVM对对象的控制方式,接下来就让我们进入




Java引用的分类


Java对象主要的引用类型分为:强引用(new)软引用(soft)、弱引用(weak)、虚引用(plantom)、终结器引用(finalizer)


image.png




强引用


只要能够通过GC Roots的引用链找到就不会被垃圾回收,也就是说只有所有的GC Roots对象都不通过强引用引用该对象的时候,该对象才能被垃圾回收,否则即使出现OOM,也不能够回收对象。



案例分析


设置jvm的内存为20M:


image.png


public class Test2 {
    public static final int _4MB=4*1024*1024;
    public static void main(String[] args) throws IOException {
        List<byte[]> list =new ArrayList<>();
        for(int i=0;i<5;i++){
            list.add(new byte[_4MB]);
        }
        System.in.read();
    }
}
复制代码



运行上面的程序会出现内存溢出问题,也就是说有的内存即使不太重要依旧会被占用:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at pers.zhb.test.Test2.main(Test2.java:12)
复制代码





软引用


Java中使用SoftRefence对象来表示软引用,如果某个对象与软引用关联,那么JVM只会在内存不足的情况下回收该对象





案例分析


image.png


public class Test2 {
    private static final int _4MB=4*1024*1024;
    public static void main(String[] args) throws IOException {
        soft();
    }
    public static void soft(){
        List<SoftReference<byte[]>> list=new ArrayList<>();
        for(int i=0;i<5;i++){
            SoftReference<byte[]> reference=new SoftReference<>(
            new byte[_4MB]);
            System.out.println(reference.get());
            list.add(reference);
            System.out.println(list.size());
        }
        System.out.println("循环结束:"+list.size());
        for(SoftReference<byte[]> reference:list){
            System.out.println(reference.get());
        }
    }
}
复制代码



运行结果:

[B@1b6d35861[B@4554617c2[B@74a144823[GC (Allocation Failure) [PSYoungGen: 1744K->488K(6144K)] 14032K->12932K(19968K), 0.0427814 secs] [Times: user=0.00 sys=0.00, real=0.05 secs] 
[B@1540e19d4[GC (Allocation Failure) --[PSYoungGen: 4696K->4696K(6144K)] 17140K->17140K(19968K), 0.0030222 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 4696K->4574K(6144K)] [ParOldGen: 12444K->12414K(13824K)] 17140K->16988K(19968K), [Metaspace: 3118K->3118K(1056768K)], 0.0095409 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) --[PSYoungGen: 4574K->4574K(6144K)] 16988K->16996K(19968K), 0.0010492 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 4574K->0K(6144K)] [ParOldGen: 12422K->587K(8704K)] 16996K->587K(14848K), [Metaspace: 3118K->3118K(1056768K)], 0.0076471 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
[B@677327b65循环结束:5nullnullnullnull[B@677327b6HeapPSYoungGen  total 6144K, used 4546K [0x00000000ff980000, 0x0000000100000000, 0x0000000100000000)eden space 5632K, 80% used [0x00000000ff980000,0x00000000ffdf0940,0x00000000fff00000)from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)ParOldGen  total 8704K, used 587K [0x00000000fec00000, 0x00000000ff480000, 0x00000000ff980000)object space 8704K, 6% used [0x00000000fec00000,0x00000000fec92d20,0x00000000ff480000)Metaspace  used 3224K, capacity 4500K, committed 4864K, reserved 1056768Kclass space used 349K, capacity 388K, committed 512K, reserved 1048576K复制代码



结论


可以看出,在第四次和第五次的时候就触发了垃圾回收,在对集合进行遍历后只有第五个软引用对象存在,也就是说在内存不足的时候将前面的虚引用的对象进行了垃圾回收



软引用与引用队列


public class Test2 {
    private static final int _4MB=4*1024*1024;
    public static void main(String[] args) throws IOException {
        List<SoftReference<byte[]>> list=new ArrayList<>();
        //引用队列
        ReferenceQueue<byte[]> queue=new ReferenceQueue<>();
        for(int i=0;i<5;i++){
            //关联了引用队列,当软引用所关联的byte数组被回收时,软引用自己会加入到引用队列中
            SoftReference<byte[]> reference=new SoftReference<>(new byte[_4MB],queue);
            System.out.println(reference.get());
            list.add(reference);
            System.out.println(list.size());
        }
        //从队列获取无用的软引用对象并移除
        Reference<? extends byte[]> poll=queue.poll();
        while(poll!=null){
            list.remove(poll);
            poll=queue.poll();
        }
        System.out.println("循环结束");
        for(SoftReference<byte[]> reference:list){
            System.out.println(reference.get());
        }
    }
}
复制代码




测试

[B@1b6d35861[B@4554617c2[B@74a144823[GC (Allocation Failure) [PSYoungGen: 1866K->488K(6144K)] 14154K->12964K(19968K), 0.0010895 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@1540e19d4[GC (Allocation Failure) --[PSYoungGen: 4696K->4696K(6144K)] 17172K->17180K(19968K), 0.0011250 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 4696K->4564K(6144K)] [ParOldGen: 12484K->12458K(13824K)] 17180K->17022K(19968K), [Metaspace: 3228K->3228K(1056768K)], 0.0055610 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) --[PSYoungGen: 4564K->4564K(6144K)] 17022K->17030K(19968K), 0.0009186 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 4564K->0K(6144K)] [ParOldGen: 12466K->620K(8704K)] 17030K->620K(14848K), [Metaspace: 3228K->3228K(1056768K)], 0.0065061 secs] [Times: user=0.00 sys=0.01, real=0.01 secs] 
[B@677327b65循环结束[B@677327b6HeapPSYoungGen      total 6144K, used 4546K [0x00000000ff980000, 0x0000000100000000, 0x0000000100000000)eden space 5632K, 80% used [0x00000000ff980000,0x00000000ffdf0918,0x00000000fff00000)from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)ParOldGen       total 8704K, used 620K [0x00000000fec00000, 0x00000000ff480000, 0x00000000ff980000)object space 8704K, 7% used [0x00000000fec00000,0x00000000fec9b160,0x00000000ff480000)Metaspace       used 3237K, capacity 4500K, committed 4864K, reserved 1056768Kclass space    used 351K, capacity 388K, committed 512K, reserved 1048576K复制代码



弱引用


如果某个对象与弱引用关联,那么当JVM在进行垃圾回收时,无论内存是否充足,都会回收此类对象


image.png


案例分析


public class Test2 {
    private static final int _4MB = 4 * 1024 * 1024;
    public static void main(String[] args) throws IOException {
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            WeakReference<byte[]> reference = new WeakReference<>(new byte[_4MB]);
            list.add(reference);
            for (WeakReference<byte[]> w : list) {
                System.out.println(w.get());
            }
            System.out.println();
        }
        System.out.println("循环结束:" + list.size());
    }
}
复制代码



测试

[B@1b6d3586
[B@1b6d3586
[B@4554617c
[B@1b6d3586
[B@4554617c
[B@74a14482
[GC (Allocation Failure) [PSYoungGen: 1865K->488K(6144K)] 14153K->12992K(19968K), 0.0014680 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@1b6d3586
[B@4554617c
[B@74a14482
[B@1540e19d
[GC (Allocation Failure) [PSYoungGen: 4809K->496K(6144K)] 17313K->13044K(19968K), 0.0013554 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@1b6d3586
[B@4554617c
[B@74a14482
null
[B@677327b6
[GC (Allocation Failure) [PSYoungGen: 4702K->472K(6144K)] 17250K->13020K(19968K), 0.0017348 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@1b6d3586
[B@4554617c
[B@74a14482
null
null
[B@14ae5a5
[GC (Allocation Failure) [PSYoungGen: 4678K->488K(6144K)] 17226K->13036K(19968K), 0.0016909 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@1b6d3586
[B@4554617c
[B@74a14482
null
null
null
[B@7f31245a
[GC (Allocation Failure) [PSYoungGen: 4694K->488K(6144K)] 17242K->13044K(19968K), 0.0008993 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@1b6d3586
[B@4554617c
[B@74a14482
null
null
null
null
[B@6d6f6e28
[GC (Allocation Failure) [PSYoungGen: 4694K->472K(5120K)] 17250K->13028K(18944K), 0.0011246 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@1b6d3586
[B@4554617c
[B@74a14482
null
null
null
null
null
[B@135fbaa4
[GC (Allocation Failure) [PSYoungGen: 4657K->32K(5632K)] 17214K->13012K(19456K), 0.0012595 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 32K->0K(5632K)] [ParOldGen: 12980K->638K(7680K)] 13012K->638K(13312K), [Metaspace: 3232K->3232K(1056768K)], 0.0084781 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
null
null
null
null
null
null
null
null
null
[B@45ee12a7
循环结束:10
Heap
PSYoungGen      total 5632K, used 4371K [0x00000000ff980000, 0x0000000100000000, 0x0000000100000000)
eden space 4608K, 94% used [0x00000000ff980000,0x00000000ffdc4e70,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen       total 7680K, used 638K [0x00000000fec00000, 0x00000000ff380000, 0x00000000ff980000)
object space 7680K, 8% used [0x00000000fec00000,0x00000000fec9fa38,0x00000000ff380000)
Metaspace       used 3239K, capacity 4500K, committed 4864K, reserved 1056768K
class space    used 351K, capacity 388K, committed 512K, reserved 1048576K
复制代码





虚引用


创建的时候会关联一个引用队列


例如


创建ByteBuffer的时候会创建一个名为Cleaner的虚引用对象当ByteBuffer没有被强引用所引用就会被jvm垃圾回收,虚引用Cleaner就会进入引用队列,会有专门的线程扫描引用队列,被发现后会调用直接内存地址的方法将直接内存释放掉,保证直接内存不会导致内存泄漏


应用场景


可以用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾回收器回收之前会收到一条系统通知。



实例


Tracker(String path, FileDeleteStrategy deleteStrategy, Object marker,
   ReferenceQueue<? super Object> queue) {
    //marker是具体的虚引用对象
    super(marker, queue);
    this.path = path;
    this.deleteStrategy = deleteStrategy == null ?    
    FileDeleteStrategy.NORMAL : deleteStrategy;
}
复制代码



结论


虚引用是所有引用类型中最弱的,一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获得一个对象实例,一个持有虚引用的对象,和没有引用几乎是一样的,随时可能被垃圾回收器回收。当试图通过虚引用的get()方法取得强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。




终结器引用


创建的时候会关联一个引用队列,当A4对象没有被强引用所引用时,A4被垃圾回收的时候,会将终结器引用放入到一个引用队列(被引用对象暂时还没有被垃圾回收),有专门的线程(优先级较低,可能会造成对象迟迟不被回收)扫描引用队列并调用finallize()方法,判断是否仍可实现可达性机制,真正执行GC的时候才会回收掉被引用对象




总结


  • 强引用:从来不会被回收


  • 软引用:当内存不足时会被回收


  • 弱引用:正常垃圾回收时回收


  • 虚引用:任何时刻都会被垃圾回收器回收





目录
打赏
0
0
0
0
379
分享
相关文章
【原理】【Java并发】【synchronized】适合中学者体质的synchronized原理
本文深入解析了Java中`synchronized`关键字的底层原理,从代码块与方法修饰的区别到锁升级机制,内容详尽。通过`monitorenter`和`monitorexit`指令,阐述了`synchronized`实现原子性、有序性和可见性的原理。同时,详细分析了锁升级流程:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,结合对象头`MarkWord`的变化,揭示JVM优化锁性能的策略。此外,还探讨了Monitor的内部结构及线程竞争锁的过程,并介绍了锁消除与锁粗化等优化手段。最后,结合实际案例,帮助读者全面理解`synchronized`在并发编程中的作用与细节。
50 8
【原理】【Java并发】【synchronized】适合中学者体质的synchronized原理
|
24天前
|
【原理】【Java并发】【volatile】适合初学者体质的volatile原理
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是写出高端的CRUD应用。2025年,我正在沉淀自己,博客更新速度也在加快。在这里,我会分享关于Java并发编程的深入理解,尤其是volatile关键字的底层原理。 本文将带你深入了解Java内存模型(JMM),解释volatile如何通过内存屏障和缓存一致性协议确保可见性和有序性,同时探讨其局限性及优化方案。欢迎订阅专栏《在2B工作中寻求并发是否搞错了什么》,一起探索并发编程的奥秘! 关注我,点赞、收藏、评论,跟上更新节奏,让我们共同进步!
92 8
【原理】【Java并发】【volatile】适合初学者体质的volatile原理
JVM实战—1.Java代码的运行原理
本文介绍了Java代码的运行机制、JVM类加载机制、JVM内存区域及其作用、垃圾回收机制,并汇总了一些常见问题。
JVM实战—1.Java代码的运行原理
重学Java基础篇—Java对象创建的7种核心方式详解
本文全面解析了Java中对象的创建方式,涵盖基础到高级技术。包括`new关键字`直接实例化、反射机制动态创建、克隆与反序列化复用对象,以及工厂方法和建造者模式等设计模式的应用。同时探讨了Spring IOC容器等框架级创建方式,并对比各类方法的适用场景与优缺点。此外,还深入分析了动态代理、Unsafe类等扩展知识及注意事项。最后总结最佳实践,建议根据业务需求选择合适方式,在灵活性与性能间取得平衡。
50 3
Java对象创建和访问
Java对象创建过程包括类加载检查、内存分配(指针碰撞或空闲列表)、内存初始化、对象头设置及初始化方法执行。访问方式有句柄和直接指针两种,前者稳定但需额外定位,后者速度快。对象创建涉及并发安全、垃圾回收等机制。
Java对象创建和访问
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
【JAVA】生成accessToken原理
在Java中,生成accessToken用于身份验证和授权,确保合法用户访问受保护资源。流程包括:1. 身份验证(如用户名密码、OAuth 2.0);2. 生成唯一且安全的令牌;3. 设置令牌有效期并存储;4. 客户端传递令牌,服务器验证其有效性。常见场景为OAuth 2.0协议,涉及客户端注册、用户授权、获取授权码和换取accessToken。示例代码展示了使用Apache HttpClient库模拟OAuth 2.0获取accessToken的过程。
Java中判断一个对象是否是空内容
在 Java 中,不同类型的对象其“空内容”的定义和判断方式各异。对于基本数据类型的包装类,空指对象引用为 null;字符串的空包括 null、长度为 0 或仅含空白字符,可通过 length() 和 trim() 判断;集合类通过 isEmpty() 方法检查是否无元素;数组的空则指引用为 null 或长度为 0。
Java快速入门之类、对象、方法
本文简要介绍了Java快速入门中的类、对象和方法。首先,解释了类和对象的概念,类是对象的抽象,对象是类的具体实例。接着,阐述了类的定义和组成,包括属性和行为,并展示了如何创建和使用对象。然后,讨论了成员变量与局部变量的区别,强调了封装的重要性,通过`private`关键字隐藏数据并提供`get/set`方法访问。最后,介绍了构造方法的定义和重载,以及标准类的制作规范,帮助初学者理解如何构建完整的Java类。
|
2月前
|
Object取值转java对象
通过本文的介绍,我们了解了几种将 `Object`类型转换为Java对象的方法,包括强制类型转换、使用 `instanceof`检查类型和泛型方法等。此外,还探讨了在集合、反射和序列化等常见场景中的应用。掌握这些方法和技巧,有助于编写更健壮和类型安全的Java代码。
96 17
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等