JVM系列(四) JVM引用-软引用关系

上一篇文章我们讲解了 什么是jvm垃圾,什么样的对象才会被称为垃圾 会被回收, 顺便讲到了 引用关系,今天我们介绍一下第二种引用关系 软引用

1 软引用

通过SoftReference引用对象时,这个引用就是软引用。软引用是还有点用,但是并不是必须的对象

在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收, 如果第二次回收还没有足够的内存,就会抛出内存溢出异常。

软引用自身也是一个对象(SoftReference类型的对象),也会占用内存空间, Java中的软引用是通过 SoftReference类来实现的

2.软引用测试

设置JVM参数

-verbose:gc -Xmx10M -Xms10M  -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError 
  • Xmx10M 最大堆内存 10M
  • Xms10M 最小堆内存 10M

image.png

2.1 内存充足, 不会销毁软引用对象

创建一个 FinalObj对象

public class MyFinalObj {

    //一般开发中不用调用这个方法
    @Override
    protected void finalize() throws Throwable {
        System.out.println(Thread.currentThread().getName() + "\t" + "---方法调用 finalize  ....");
    }

}

新建Test类, 类中手动触发 System.gc();

@Slf4j
public class JvmSoftTest {

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

        SoftReference<MyFinalObj> soft = new SoftReference<>(new MyFinalObj());
        log.info("gc 前 内存充足=======" + soft.get());

        //手动触发 gc
        System.gc();

        Thread.sleep(1000);

        log.info("gc 后 内存充足=======" + soft.get());
    }
}

执行,打印日志如下:

[GC (Allocation Failure) [PSYoungGen: 2048K->512K(2560K)] 2048K->728K(9728K), 0.0010054 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2560K->512K(2560K)] 2776K->1006K(9728K), 0.0012670 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2557K->512K(2560K)] 3051K->1450K(9728K), 0.0008688 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
21:32:10.808 [main] INFO com.jzj.tdmybatis.util.JvmSoftTest - gc 前 内存充足=======com.jzj.tdmybatis.util.MyFinalObj@5d6f64b1
[GC (System.gc()) [PSYoungGen: 1493K->512K(2560K)] 2431K->1682K(9728K), 0.0013372 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 512K->0K(2560K)] [ParOldGen: 1170K->1255K(7168K)] 1682K->1255K(9728K), [Metaspace: 4744K->4744K(1056768K)], 0.0105563 secs] [Times: user=0.04 sys=0.00, real=0.01 secs] 
21:32:11.827 [main] INFO com.jzj.tdmybatis.util.JvmSoftTest - gc 后 内存充足=======com.jzj.tdmybatis.util.MyFinalObj@5d6f64b1
Heap
 PSYoungGen      total 2560K, used 595K [0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 2048K, 29% used [0x00000007bfd00000,0x00000007bfd94f90,0x00000007bff00000)
  from space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
  to   space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
 ParOldGen       total 7168K, used 1255K [0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000)
  object space 7168K, 17% used [0x00000007bf600000,0x00000007bf739ff0,0x00000007bfd00000)
 Metaspace       used 4851K, capacity 5068K, committed 5248K, reserved 1056768K
  class space    used 538K, capacity 564K, committed 640K, reserved 1048576K

可以看到 触发gc后 对象依旧存在, 所以说 软引用的对象 在内存充足的时候是不会被回收的

image.png

2.2 内存不充足, gc销毁软引用对象

现在我们相同的方法 创建软引用对象, 然后 根据 jvm设置的 内存大小, 创建一个比较大的对象8M, 占满内存, 然后看看 软引用的对象是否会被 gc回收

  • 为啥要创建8M的对象? 因为 10M内存, 1:2 年轻/老年, 老年代应该在 7M左右
  • SYoungGen total 2560K
  • eden space 2048K, 49% used
  • from space 512K, 100% used
  • to space 512K, 0% used
  • ParOldGen total 7168K
  • 创建8M就是为了 老年代也放不下, 直接让他oom
public static void main(String[] args) throws Exception {
    //设置JVM参数 -Xms10M -Xmx10M
    SoftReference<MyFinalObj> soft = new SoftReference<>(new MyFinalObj());
    log.info("gc 前 内存充足=======" + soft.get());

    try {
        log.info("gc 前 创建8M大对象 =======" );
        byte[] bytes = new byte[8 * 1024 * 1024];
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        log.info("gc 后 内存溢出 =======" + soft.get());
    }

}

执行,打印日志如下:

21:45:24.131 [main] INFO com.jzj.tdmybatis.util.JvmSoftTest - gc 前 内存充足=======com.jzj.tdmybatis.util.MyFinalObj@5d6f64b1
21:45:24.134 [main] INFO com.jzj.tdmybatis.util.JvmSoftTest - gc 前 创建8M大对象 =======
[GC (Allocation Failure) [PSYoungGen: 1477K->480K(2560K)] 2437K->1679K(9728K), 0.0013545 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 480K->480K(2560K)] 1679K->1687K(9728K), 0.0011450 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 480K->0K(2560K)] [ParOldGen: 1207K->1256K(7168K)] 1687K->1256K(9728K), [Metaspace: 4738K->4738K(1056768K)], 0.0083094 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 1256K->1256K(8704K), 0.0011856 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 1256K->1181K(7168K)] 1256K->1181K(8704K), [Metaspace: 4738K->4738K(1056768K)], 0.0085159 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid23687.hprof ...
Finalizer	---方法调用 finalize  ....
Heap dump file created [2325085 bytes in 0.010 secs]
21:45:24.165 [main] INFO com.jzj.tdmybatis.util.JvmSoftTest - gc 后 内存溢出 =======null

  • 对象OOM后 , 销毁了 软引用的对象, 变为了Null
  • 内存不够用的情况下, 会销毁 软引用对象
  • 所有的对象都被销毁, 调用了 finalize 方法

image.png

3. 应用场景

内存敏感的数据缓存机制,如图片、网页缓存, 实际项目中对于存储图片资源这些并不是很重要的内容时并且内存资源十分紧张,我们可以使用软引用来进行。

系统加载的时候把图片及缓存处理好, 系统一旦资源不足, 就会把这块内存回收, 不会影响性能, 当系统资源充足的时候, 软引用的一直存在, 可以直接取用, 方便快捷


本文我们讲述了 软引用的使用方法及适用场景, 以及软引用对象的垃圾回收机制,便于我们在实际的项目中进行软引用的实战

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值