【JVM】如何理解强引用、软引用、弱引用、虚引用?
整体架构

强引用
强引用是默认支持,当内存不足的时候,JVM开始垃圾回收,对于强引用的对象,就算是出现了OOM也不会回收对象。
强引用是最常见的普通对象引用,只要还有强引用指向对象,对象就存活,垃圾回收器不会处理存活对象。一般把一个对象赋给一个引用变量,这个引用变量就是强引用。当一个对象被强引用变量所引用,它就处于可达状态,是不会被垃圾回收的,即使之后都不会再用到了,也不会回收。因此强引用是造成Java内存泄漏的主要原因之一。
关于Java内存泄漏的详细内容,可以参考这篇博客:https://blog.csdn.net/m0_38110132/article/details/81986334。
对于一个普通对象,如果没有其他引用关系,只要超过了引用的作用域或者显式地将相应的强引用赋值为null,一般认为就是可以被垃圾回收了。(具体的回收时机看垃圾回收策略)
下例中,b就是强引用。
public static void main(String[] args) {
Object a = new Object();
Object b = a;
a = null;
System.out.println(b);//java.lang.Object@4554617c
}
软引用
软引用是一种相对强引用弱化了一些的引用,用java.lang.ref.SoftReference实现,可以让对象豁免一些垃圾收集。当系统内存充足的时候,不会被回收;当系统内存不足的时候,会被回收。
软引用一般用于对内存敏感的程序中,比如高速缓存。
import java.lang.ref.SoftReference;
public class SoftReferenceDemo {
public static void main(String[] args) {
Object a = new Object();
SoftReference<Object> softReference = new SoftReference<>(a);//软引用
//a和软引用指向同一个对象
System.out.println(a);//java.lang.Object@4554617c
System.out.println(softReference.get());//java.lang.Object@4554617c
//内存够用,软引用不会被回收
a = null;
System.gc();//内存够用不会自动gc,手动唤醒gc
System.out.println(a);//null
System.out.println(softReference.get());//java.lang.Object@4554617c
//内存不够用时
try{
//配置Xms和Xmx为5MB
byte[] bytes = new byte[1024*1024*30];//设置30MB超内存
}catch (Throwable e){
e.printStackTrace();
}finally {
System.out.println(a);//null
System.out.println(softReference.get());//null
}
}
}
使用场景
一个应用需要读取大量的本地图片,如果每次读取都从硬盘读取会严重影响性能,如果一次性全部加载到内存,内存可能会溢出。
可以使用软引用解决这个问题,使用一个HashMap来保存图片路径和图片对象管理的软引用之间的映射关系,内存不足时,JVM会自动回收缓存图片对象的占用空间,有效地避免了OOM(Out Of Memory)问题。
Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>
弱引用
弱引用需要用java.lang.ref.WeakReference实现,它比软引用的生存期更短,对于弱引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否够,都会回收该对象的占用内存。
import java.lang.ref.WeakReference;
public class SoftReferenceDemo {
public static void main(String[] args) {
Object a = new Object();
WeakReference<Object> softReference = new WeakReference<>(a);//软引用
//a和弱引用指向同一个对象
System.out.println(a);//java.lang.Object@4554617c
System.out.println(softReference.get());//java.lang.Object@4554617c
//内存够用,弱引用也会被回收
a = null;
System.gc();//内存够用不会自动gc,手动唤醒gc
System.out.println(a);//null
System.out.println(softReference.get());//null
}
}
关于WeakHashMap

public static void weakHashMapTest() {
Integer key = new Integer(1);
String value = "李四";
Map<Integer,String> weakHashMap = new WeakHashMap();
weakHashMap.put(key, value);
System.out.println(weakHashMap);//{1=李四}
key = null;
System.gc();
System.out.println(weakHashMap);//{}
}
public static void hashMapTest() {
HashMap<Integer,String> map = new HashMap<>();
Integer key = 1;
String value = "张三";
map.put(key,value);
System.out.println(map);//{1=张三}
key = null;
System.gc();
System.out.println(map);//{1=张三}
}
在HashMap中,键被置为null,唤醒gc后,不会垃圾回收键为null的键值对。但是在WeakHashMap中,键被置为null,唤醒gc后,键为null的键值对会被回收。
虚引用
虚引用要通过java.lang.ref.PhantomReference类来实现,虚引用不会决定对象的生命周期,如果一个对象只有虚引用,就相当于没有引用,在任何时候都可能会被垃圾回收器回收。它不能单独使用也不能访问对象,虚引用必须和引用队列联合使用。
虚引用的主要作用是跟踪对象被垃圾回收的状态,仅仅是提供一种确保对象被finalize以后,做某些事情的机制。
PhantomReference的get方法总是返回null,因此无法访问对应的引用对象,设置虚引用关联唯一的目的是在对象被收集器回收的时候收到一个系统通知,或者后续添加进一步的处理。Java允许使用finalize()方法在垃圾回收器将对象从内存中清理出去之前做一些必要的清理工作。【例如实现一个监控对象的通知机制】
引用队列
WeakReference和ReferenceQueue的联合使用效果:
public static void weakReferenceTest() {
Object a = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<>();
WeakReference<Object> weakReference = new WeakReference<>(a,queue);
System.out.println(a);//java.lang.Object@4554617c
System.out.println(weakReference.get());//java.lang.Object@4554617c
System.out.println(queue.poll());//null
System.out.println("-------------------");
a = null;
System.gc();
System.out.println(a);//null
System.out.println(weakReference.get());//null
//虚引用在回收之前被加入到了引用队列中
System.out.println(queue.poll());//java.lang.ref.WeakReference@74a14482
}
PhantomReference和ReferenceQueue的联合使用效果:
public static void phantomReferenceTest() {
Object a = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(a,queue);
System.out.println(a);//java.lang.Object@4554617c
System.out.println(phantomReference.get());//null
System.out.println(queue.poll());//null
System.out.println("-------------------");
a = null;
System.gc();
System.out.println(a);//null
System.out.println(phantomReference.get());//null
//引用在回收之前被加入到了引用队列中
System.out.println(queue.poll());//java.lang.ref.WeakReference@74a14482
}
总结

强引用:不回收。
软引用:内存不够就回收。
弱引用:一定回收。
虚引用:一定回收,get出来就是null,引用形同虚设,主要和引用队列联合使用,在finalize之前会被放到引用队列中。
与根对象没有引用关系的:引用不可达,一定回收。
【JVM】如何理解强引用、软引用、弱引用、虚引用?的更多相关文章
- jvm系列 (四) ---强、软、弱、虚引用
java引用 目录 jvm系列(一):jvm内存区域与溢出 jvm系列(二):垃圾收集器与内存分配策略 jvm系列(三):锁的优化 我的博客目录 为什么将引用分为不同的强度 因为我们需要实现这样一种情 ...
- Java中四种引用:强、软、弱、虚引用
这篇文章非常棒:http://alinazh.blog.51cto.com/5459270/1276173 Java中四种引用:强.软.弱.虚引用 1.1.强引用当我们使用new 这个关键字创建对象时 ...
- Java:对象的强、软、弱、虚引用
转自: http://zhangjunhd.blog.51cto.com/113473/53092 1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无 ...
- Java:对象的强、软、弱和虚引用
1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK ...
- Java对象的强、软、弱和虚引用详解
1.对象的强.软.弱和虚引用 转自:http://zhangjunhd.blog.51cto.com/113473/53092/ 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无 ...
- java基础知识再学习--集合框架-对象的强、软、弱和虚引用
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://zhangjunhd.blog.51cto.com/113473/53092 本文 ...
- Java对象的强、软、弱和虚引用原理+结合ReferenceQueue对象构造Java对象的高速缓存器
//转 http://blog.csdn.net/lyfi01/article/details/6415726 1.Java对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变 ...
- Java:对象的强、软、弱和虚引用[转]
原文链接:http://zhangjunhd.blog.51cto.com/113473/53092/ 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法 ...
- Java对象的强、软、弱和虚引用
本文介绍Java对象的强.软.弱和虚引用的概念.应用及其在UML中的表示. 1.Java对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象 ...
- 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用
垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...
随机推荐
- C. Coffee Break 贪心 思维 有点难 有意思
C. Coffee Break 这个贪心之前好像写过,还是感觉挺难的,有点不会写. 这个题目大意是:给你一个数列n个元素,然后给你一天的时间,给你一个间隔时间d, 问你最少要用多少天可以把这个数列的所 ...
- S - Making the Grade POJ - 3666 结论 将严格递减转化成非严格的
S - Making the Grade POJ - 3666 这个题目要求把一个给定的序列变成递增或者递减序列的最小代价. 这个是一个dp,对于这个dp的定义我觉得不是很好想,如果第一次碰到的话. ...
- java线程池原理解析
五一假期大雄看了一本<java并发编程艺术>,了解了线程池的基本工作流程,竟然发现线程池工作原理和互联网公司运作模式十分相似. 线程池处理流程 原理解析 互联网公司与线程池的关系 这里用一 ...
- C#黔驴技巧之去重(Distinct)
前言 关于C#中默认的Distinct方法在什么情况下才能去重,这个就不用我再多讲,针对集合对象去重默认实现将不再满足,于是乎我们需要自定义实现来解决这个问题,接下来我们详细讲解几种常见去重方案,孰好 ...
- spring源码解析--上
本文是作者原创,版权归作者所有.若要转载,请注明出处. 首先是配置类 package com.lusai.config; import org.springframework.context.anno ...
- HBuilderX 打包 uniapp 项目 图片路径不显示(不正确)
打包h5项目本地服务运行正常 部署后页面显示空白 在根目录manifest.json中配置h5下的publicPath即可 "h5" : { "template" ...
- 安卓APP承载网页(WebView)
安卓APP自身如何打开网页,如何制作一个简单的浏览器,WebView在其中将是一个重要的角色.WebView是一个基于WebKit引擎.展现Web页面的控件. Webview 是一个基于webkit引 ...
- java调用oracle存储过程返回多条结果集
oracle版本:11g oracle存储过程,使用游标的方式返回多行.多列数据集合: CREATE OR REPLACE PROCEDURE SP_DATA_TEST( /*P_ID IN INT, ...
- 蒲公英 · JELLY技术周刊 Vol.07: EcmaScript 2020 -- 所有你想要知道的都在这
「蒲公英」期刊,每周更新,我们专注于挖掘「基础技术.工程化.跨端框架技术.图形编程.服务端开发.桌面开发.人工智能」等多个大方向的业界热点,并加以专业的解读:不仅如此,我们还精选凹凸技术文章,向大家呈 ...
- node 之 ... 扩展运算符报错
使用pm2的遇到的问题:(实际上是 node 版本不一致导致的问题) 描述:sudo 下的node版本和 全局下的node版本不一致导致...扩展运算符报错. 实例: { "apps&quo ...