【案例演示】JVM之强引用、软引用、弱引用、虚引用
1.背景
想要理解对象什么时候回收,就要理解到对象引用这个概念,于是有了下文
2.java中引用对象结构图
3.引用详解
3.1.什么是强引用
a.当内存不足,JVM开始垃圾回收,对于强引用的对象,就算是出现了00M也不会对该对象进行回收,死都不收。
b.强引用是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象。
在Java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。
当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到JVM也不会回收。
因此强引用是造成Java内存泄漏的主要原因之一
c.对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为null,一般认为就是可以被垃圾收集的了〈当然具体回收时机还是要看垃圾收集策略)。
案例:


package com.wfd360.demo03GC.referDemo; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 12:12
* @description
*/
public class StrongRefer {
/**
* 强引用的理解
*
* @param args
*/
public static void main(String[] args) {
Object obj1 = new Object();
// 建立强引用
Object obj2 = obj1;
// 观察obj1 和 obj2 的各种内存地址
System.out.println("obj1=" + obj1);
System.out.println("obj2=" + obj2);
// obj1创建可以回收的条件
obj1 = null;
// gc回收
System.gc();
// 观察各对象情况
System.out.println("obj1=" + obj1);
System.out.println("obj2=" + obj2);
}
}
从测试结果课程看出,obj1的实际对象别没有回收;
3.2.什么是软引用
a.软引用是用来描述一些还有用但并非必需的对象,需要用java.lang.ref.SoftReference类来实现。
b.对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK1.2之后,提供了Soft Reference类来实现软引用。
c.软引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够用的时候就保留,不够用就回收!
案例:


package com.wfd360.demo03GC.referDemo; import java.lang.ref.SoftReference; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 12:12
* @description
*/
public class SoftRefer { /**
* 软引用的理解
* 通过设置jvm参数,在不同的条件下观察
*
* @param -Xms5m -Xmx5m -XX:+PrintGCDetails
* @param args
*/
public static void main(String[] args) {
// 测试内存充足(不回收软引用)
//testSoftReferNOGc();
// 测试内存不充足(回收软引用)
testSoftReferGc();
} /**
* 模拟内存充足的情况
*/
public static void testSoftReferNOGc() {
Object obj1 = new Object();
// 建立软引用
SoftReference softRefer = new SoftReference<>(obj1);
// 观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
// obj1创建可以回收的条件
obj1 = null;
// gc回收
System.gc();
// 再次观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
} /**
* 模拟内存不足
* 1.设置较小的堆内存
* 2.创建大对象
* 3.jvm参
* -Xms5m -Xmx5m -XX:+PrintGCDetails
*/
public static void testSoftReferGc() {
Object obj1 = new Object();
// 建立软引用
SoftReference softRefer = new SoftReference<>(obj1);
// 观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
// obj1创建可以回收的条件
obj1 = null;
try {
byte[] bytes = new byte[6 * 1024 * 1024];
} catch (Throwable e) {
System.out.println("===============>error:" + e.getMessage());
} finally {
// 再次观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
}
}
}
内存充足测试结果:
内存不充足测试结果:
实际案例
假如有一个应用需要读取大量的本地数据(图片、通讯率、临时文件等):
如果每次读取数据都从硬盘读取则会严重影响性能,
如果一次性全部加载到内存中又可能造成内存溢出。
此时使用软引用可以解决这个问题。
设计思路是:用一个HashMap来保存数据的路径和相应数据对象关联的软引用之间的映射关系,在内存不足时,
JVM会自动回收这些缓存数据对象所占用的空间,从而有效地避免了00M的问题。
Map<String,SoftReference>imageCache=new HashMap<String,SoftReference>();
3.3.什么是弱引用
a.弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。
b..当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供广Weak Reference类来实现弱引用。
c.弱引用需要用Java.lang.ref.WeakReference类来实现,它比软引用的生存期更短.
案例:


package com.wfd360.demo03GC.referDemo; import java.lang.ref.WeakReference; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 12:12
* @description
*/
public class WeakRefer { /**
* 弱引用的理解
*
* @param args
*/
public static void main(String[] args) {
Object obj1 = new Object();
// 建立弱引用
WeakReference softRefer = new WeakReference<>(obj1);
// 观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
// obj1创建可以回收的条件
obj1 = null;
// gc回收
System.gc();
// 再次观察内存地址
System.out.println("obj1=" + obj1);
System.out.println("softRefer=" + softRefer.get());
} }
扩展知识-WeakHashMap
查看API介绍:
测试代码:


package com.wfd360.demo03GC.referDemo; import java.util.HashMap;
import java.util.WeakHashMap; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 5:10
* @description <p>
* 弱引用引用之:WeakHashMap
* 以弱键 实现的基于哈希表的 Map。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目。
* 更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,
* 然后被回收。丢弃某个键时,其条目从映射中有效地移除,因此,该类的行为与其他的 Map 实现有所不同。
* </p>
*/
public class WeakReferMap {
/**
* 测试 HashMap 与 WeakHashMap 区别
* 测试逻辑:
* 1.创建不同的map
* 2.创建key value值
* 3.放入各自的map,并打印结果
* 4.将key设置为null,并打印结果
* 5.手动GC,并打印结果
*
* @param args
*/
public static void main(String[] args) {
hashMapMethod();
System.out.println("--------华丽的分割线--------");
weakHashMapMethod();
} /**
* HashMap测试(强引用)
*/
private static void hashMapMethod() {
HashMap<String, String> map = new HashMap<>();
String key = "key1";
String value = "HashMap-value"; map.put(key, value);
System.out.println(map); key = null;
System.out.println(map); System.gc();
System.out.println(map);
} /**
* 若引用(WeakHashMap测试)
*/
private static void weakHashMapMethod() {
WeakHashMap<String, String> map = new WeakHashMap<>();
// 注意这里的new一个字符串与直接写key="key2"对测试结果是有区别的,详细原因可以看之前讲的内存分配
String key = new String("key2");
String value = "WeakHashMap-value"; map.put(key, value);
System.out.println(map); key = null;
System.out.println(map); System.gc();
System.out.println(map); } }
测试结果:
从测试结果可以看出:弱引用的map数据已经被回收。
扩展知识-ReferenceQueue引用队列
代码:


package com.wfd360.demo03GC.referDemo; import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 7:23
* @description
*/
public class QueueRefer {
/**
* 测试弱引用回收前,把数据放入队列中
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Object obj1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue();
// 当GC释放对象内存的时候,会将引用加入到引用队列
WeakReference<Object> weakReference = new WeakReference<>(obj1, referenceQueue); System.out.println(obj1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll()); System.out.println("--------华丽的分割线--------");
obj1 = null;
System.gc();
Thread.sleep(500); System.out.println(obj1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
} }
采用弱引用的方式测试结果:
从测试结果可以看出,需要回收的对象已经进入队列。
采用软引用的方式测试结果:
从测试结果可以看出,软引用,没有到达回收的条件,并没有进行回收,也不会进入队列;
3.4.什么是虚引用
1.虚引用需要java.lang.ref.PhantomReference类来实现。
2.与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有
虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访
问对象,虚引用必须和引用队列(ReferenceQueue)联合使用。
3.虚引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后,做某些事情的
机制。PhantomReference的get方法总是返回null,因此无法访问对应的引用对象。其意义在于说明一个对象己
经进入俑finalization阶段,可以被gc回收,用来实现比finalization机制更灵活的回收操作。
4.设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加
进一步的处理。Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
代码:


package com.wfd360.demo03GC.referDemo; import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 7:44
* @description
*/
public class PhantomRefer {
/**
* 虚引用测试
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Object obj1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue();
PhantomReference<Object> phantomReference = new PhantomReference<>(obj1,referenceQueue); System.out.println(obj1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll()); System.out.println("--------华丽的分割线--------"); obj1 = null;
System.gc();
Thread.sleep(500); System.out.println(obj1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
} }
测试结果:
4.重要总结
对象是否存活判断流程:
1.可达性分析,看是否有GC Roots的引用链,如果没有将做第一次标记;
2.检查是否需要执行finalize()方法,
如果没必要(之前执行过了),直接回收内存;
如果要执行finalize()方法,这个时候对象如果再次建立引用链(唯一自救机会),对象不会被回收,否则直接回收;
总结:
1.对象回收满足两个条件:
a.没有引用链。
b.回收前会执行finalize()方法,如果执行finalize(),没有再次建立连接(如果重新与引用链上的任意对象建立连接,例如给对象赋值,该对象都不会被回收)
2.在gc回收前会执行finalize()方法,只执行一次,并且是异步执行不保证执行成功,线程优先级低
代码演示:


package com.wfd360.demo03GC.referDemo; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 06/20 8:34
* @description
*/
public class FinalizeGC {
public static FinalizeGC obj1 = null; /**
* 重写finalize方法
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("执行finalize方法");
// 自救,在回收时建立引用链
FinalizeGC.obj1 = this;
} public static void main(String[] args) throws InterruptedException {
obj1 = new FinalizeGC(); obj1 = null;
System.gc();
Thread.sleep(600);
System.out.println("第一次自救成功:"+obj1); obj1 = null;
System.gc();
Thread.sleep(600);
System.out.println("第二次自救失败,不会再次执行finalize方法:"+obj1);
}
}
测试结果:
完美!
【案例演示】JVM之强引用、软引用、弱引用、虚引用的更多相关文章
- Java中四种引用:强、软、弱、虚引用
这篇文章非常棒:http://alinazh.blog.51cto.com/5459270/1276173 Java中四种引用:强.软.弱.虚引用 1.1.强引用当我们使用new 这个关键字创建对象时 ...
- 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用
垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...
- 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.lang.ref包下还提供了3个类:SoftReference.WeakReference和Phan ...
- Java对象的强、软、弱和虚引用+ReferenceQueue
Java对象的强.软.弱和虚引用+ReferenceQueue 一.强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足 ...
随机推荐
- [SD心灵鸡汤]006.每月一则 - 2015.10
1. 贫不足羞,可羞是贫而无志. 2. 艺术的大道上荆棘丛生,这也是好事,常人望而却步,只有意志坚强的人例外. 3. 古今中外,凡成就事业,对人类有作为的无一不是脚踏实地.艰苦攀登的结果. 4. 理想 ...
- Python数据分析:pandas玩转Excel (二)
1 对Excel文件的操作 方法一: 使用xlrd库或者xlwt库进行对excel表格的操作读与写: 方法二: pandas库同样支持excel的读写操作:且更加简便. 2 pd.read_excel ...
- Jenkins在Pod中实现Docker in Docker并用kubectl进行部署
Jenkins在Pod中实现Docker in Docker并用kubectl进行部署 准备工作 安装Jenkins Jenkins的kubernetes-plugin使用方法 说明 Jenkins的 ...
- mysql去重复关键字distinct的用法
distinct的去重复的提前是表中所有列的数据完成相同时,才能把相同的数据只保留一条,并不是 distinct 列名,除去某一列相同的数据,并且 distinct要放在第一个列前面.案例如下:一个学 ...
- CAD文件打印为PDF文档
标题: CAD文件打印为PDF文档 作者: 梦幻之心星 347369787@QQ.com 标签: [CAD, 打印, 转换] 目录: 软件 日期: 2019-5-28 目录 CAD文件打印为PDF文档 ...
- 03 . Redis集群
Redis集群方案 Redis Cluster 集群模式通常具有 高可用.可扩展性.分布式.容错等特性.Redis分布式方案一般有两种 客户端分区方案 客户端 就已经决定数据会被 存储到哪个 redi ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(一)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- Redis详解(十二)------ 缓存穿透、缓存击穿、缓存雪崩
本篇博客我们来介绍Redis使用过程中需要注意的三种问题:缓存穿透.缓存击穿.缓存雪崩. 1.缓存穿透 一.概念 缓存穿透:缓存和数据库中都没有的数据,可用户还是源源不断的发起请求,导致每次请求都会到 ...
- FTP免费工具,FTP免费工具推荐!
IIS7服务器管理工具是FTP操作的客户端软件,能够作为批量操作FTP命令!同时,它还能够作为VNC的操作客户端进行VNC的相关操作!能够连接Windows和Linux的服务器和PC,并进行实时的检测 ...
- Java 第十一届 蓝桥杯 省模拟赛 19000互质的个数
问题描述 不超过19000的正整数中,与19000互质的数的个数是多少? 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将 ...