【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的垃圾回收机制 ...
随机推荐
- 第三章:Python高级编程-深入类和对象
第三章:Python高级编程-深入类和对象 Python3高级核心技术97讲 笔记 3.1 鸭子类型和多态 """ 当看到一直鸟走起来像鸭子.游泳起来像鸭子.叫起来像鸭子 ...
- Navicat12.1系列安装,破解以及破解navicat报错的解决方案
由于上课的需要,我们必须自己下载并安装 Navicat Premium 12,虽然安装过程很简单,但是安装后的navicat只能试用,并没有永久激活,然而我还想永久使用,所以就各种百度,因为不断地遇到 ...
- Z - New Year Tree CodeForces - 620E 线段树 区间种类 bitset
Z - New Year Tree CodeForces - 620E 这个题目还没有写,先想想思路,我觉得这个题目应该可以用bitset, 首先这个肯定是用dfs序把这个树转化成线段树,也就是二叉树 ...
- 12_JavaScript基础入门(2)
运算符 运算符(Operators,也翻译为操作符),是发起运算的最简单形式. 运算符的分类见仁见智,我们的课程对运算符进行如下分类: 数学运算符(Arithmetic operators) ...
- C# 中 枚举Enum 一些转换的方法整理
工作中 经常遇到枚举 的一些转换 特别是获取枚举备注等 特地整理下 方法以后使用 public void TestMethod1() { TestEnumOne colorEnum = TestE ...
- [poj1741 Tree]树上点分治
题意:给一个N个节点的带权树,求长度小于等于K的路径条数 思路:选取一个点作为根root,假设f(root)是当前树的答案,那么答案来源于两部分: (1)路径不经过root,那么就是完全在子树内,这部 ...
- 《学习笔记》.NET Core API搭建
1.创建 ASP.NET Core Web程序,记住取消HTTPS配置 2.此时一个简单的.NET Core API 架子搭建好了,细心的人可以发现Properties下面不是CS文件,确是launc ...
- javaweb学习之路(2)response
写一个简单的登录页面 1.创建一个login.jsp文件 主要内容: <form action="check.jsp" method="post"> ...
- 谈谈Java常用类库中的设计模式 - Part Ⅰ
背景 最近一口气看完了Joshua Bloch大神的Effective Java(下文简称EJ).书中以tips的形式罗列了Java开发中的最佳实践,每个tip都将其意图和要点压缩在了标题里,这种做法 ...
- elasticsearch 小总结
elasticsearch 小总结 0. 起因 距离初次写关于es的文章 https://blog.csdn.net/aca_jingru/article/details/44488703 已经过去4 ...