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之强引用、软引用、弱引用、虚引用的更多相关文章

  1. Java中四种引用:强、软、弱、虚引用

    这篇文章非常棒:http://alinazh.blog.51cto.com/5459270/1276173 Java中四种引用:强.软.弱.虚引用 1.1.强引用当我们使用new 这个关键字创建对象时 ...

  2. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

  3. Java:对象的强、软、弱、虚引用

    转自: http://zhangjunhd.blog.51cto.com/113473/53092 1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无 ...

  4. Java:对象的强、软、弱和虚引用

    1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK ...

  5. Java对象的强、软、弱和虚引用详解

    1.对象的强.软.弱和虚引用 转自:http://zhangjunhd.blog.51cto.com/113473/53092/ 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无 ...

  6. java基础知识再学习--集合框架-对象的强、软、弱和虚引用

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://zhangjunhd.blog.51cto.com/113473/53092 本文 ...

  7. Java对象的强、软、弱和虚引用原理+结合ReferenceQueue对象构造Java对象的高速缓存器

    //转 http://blog.csdn.net/lyfi01/article/details/6415726 1.Java对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变 ...

  8. Java:对象的强、软、弱和虚引用[转]

    原文链接:http://zhangjunhd.blog.51cto.com/113473/53092/ 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法 ...

  9. 详解Java中对象的软、弱和虚引用的区别

    对于大部分的对象而言,程序里会有一个引用变量来引用该对象,这是最常见的引用方法.除此之外,java.lang.ref包下还提供了3个类:SoftReference.WeakReference和Phan ...

  10. Java对象的强、软、弱和虚引用+ReferenceQueue

    Java对象的强.软.弱和虚引用+ReferenceQueue 一.强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足 ...

随机推荐

  1. NO.2 TI开发环境的搭建 SDK+Code Composer Studio

    首先我们要了解TI嵌入式开发环境 对于TI嵌入式开发,首先我们要下载SDK软件包,其次要准备编译环境Code Composer Studio. 对于SDK的下载,可以在官网浏览http://www.t ...

  2. Redis学习笔记(3)

    一.Redis的事务(transaction) 1. 事务概念 本质:本质是一组命令的集合,所有的命令按照顺序一次性.串行化地执行,不允许其他命令的加入.Redis通过MULTI.EXEC.WATCH ...

  3. [SD心灵鸡汤]004.每月一则 - 2015.08

    1.事常与人违,事总在人为. 2.骏马是跑出来的,强兵是打出来的. 3.驾驭命运的舵是奋斗.不抱有一丝幻想,不放弃一点机会,不停止一日努力. 4.如果惧怕前面跌宕的山岩,生命就永远只能是死水一潭. 5 ...

  4. vue-cli4配置文件别名

    具体步骤如下: 1.在项目中新建vue.config.js文件 注意:此文件要与src文件夹同级 : 修改此文件后,需要重启项目 2.在vue.config.js文件中配置如截图 第一个参数:是你设置 ...

  5. jQuery-显示与隐藏

    1.显示与隐藏 show([speed,easing,function]) speed:毫秒单位的时间值 hide([speed,easing,function]) 用法:元素.show()/元素.h ...

  6. 开心一下-实现一个基于Java的中文编程语言2

    https://mp.weixin.qq.com/s/VmCTvh0c7X9DjIgIMycdlw   上一篇所提到的只是使用中文写Java,而不能算作一门新的语言.作为一门中文语言,需要语言提供的关 ...

  7. Java实现 蓝桥杯 算法提高 欧拉函数(数学)

    试题 算法提高 欧拉函数 问题描述 老师出了一道难题,小酱不会做,请你编个程序帮帮他,奖金一瓶酱油: 从1-n中有多少个数与n互质? |||||╭══╮ ┌═════┐ ╭╯让路║═║酱油专用车║ ╰ ...

  8. Java实现蓝桥杯模拟递增三元组

    问题描述 在数列 a[1], a[2], -, a[n] 中,如果对于下标 i, j, k 满足 0<i<j<k<n+1 且 a[i]<a[j]<a[k],则称 a ...

  9. Java实现 LeetCode 335 路径交叉

    335. 路径交叉 给定一个含有 n 个正数的数组 x.从点 (0,0) 开始,先向北移动 x[0] 米,然后向西移动 x[1] 米,向南移动 x[2] 米,向东移动 x[3] 米,持续移动.也就是说 ...

  10. Java实现 LeetCode 165 比较版本号

    165. 比较版本号 比较两个版本号 version1 和 version2. 如果 version1 > version2 返回 1,如果 version1 < version2 返回 ...