深入浅出JVM(十四)之内存溢出、泄漏与引用

本篇文章将深入浅出的介绍Java中的内存溢出与内存泄漏并说明强引用、软引用、弱引用、虚引用的特点与使用场景

引用

在栈上的reference类型存储的数据代表某块内存地址,称reference为某内存、某对象的引用

实际上引用分为很多种,从强到弱分为:强引用 > 软引用 > 弱引用 > 虚引用

平常我们使用的引用实际上是强引用,各种引用有自己的特点,下文将一一介绍

强引用就是Java中普通的对象,而软引用、弱引用、虚引用在JDK中定义的类分别是SoftReferenceWeakReferencePhantomReference

下图是软引用、弱引用、虚引用、引用队列(搭配虚引用使用)之间的继承关系

image-20201119220035297.png

内存溢出与内存泄漏

为了更清除的描述引用之间的作用,首先需要介绍一下内存溢出和内存泄漏

当发生内存溢出时,表示JVM没有空闲内存为新对象分配空间,抛出OutOfMemoryError(OOM)

当应用程序占用内存速度大于垃圾回收内存速度时就可能发生OOM

抛出OOM之前通常会进行Full GC,如果进行Full GC后依旧内存不足才抛出OOM

JVM参数-Xms10m -Xmx10m -XX:+PrintGCDetails

image-20210504133922780.png

内存溢出可能发生的两种情况:

  1. 必须的资源确实很大,堆内存设置太小 (通过-Xmx来调整)
  1. 发生内存泄漏,创建大量对象,且生命周期长,不能被回收

内存泄漏Memory Leak: 对象不会被程序用到了,但是不能回收它们

对象不再使用并且不能回收就会一直占用空间,大量对象发生内存泄漏可能发生内存溢出OOM

广义内存泄漏:不正确的操作导致对象生命周期变长

  1. 单例中引用外部对象,当这个外部对象不用了,但是因为单例还引用着它导致内存泄漏
  2. 一些需要关闭的资源未关闭导致内存泄漏
强引用

强引用是程序代码中普遍存在的引用赋值,比如List list = new ArrayList();

只要强引用在可达性分析算法中可达时,垃圾收集器就不会回收该对象,因此不当的使用强引用是造成Java内存泄漏的主要原因

软引用

当内存充足时不会回收软引用

只有当内存不足时,发生Full GC时才将软引用进行回收,如果回收后还没充足内存则抛出OOM异常

JVM中针对不同的区域(年轻代、老年代、元空间)有不同的GC方式,Full GC的回收区域为整个堆和元空间

软引用使用SoftReference

内存充足情况下的软引用

     public static void main(String[] args) {int[] list = new int[10];SoftReference listSoftReference = new SoftReference(list);//[I@61bbe9baSystem.out.println(listSoftReference.get());}

内存不充足情况下的软引用(JVM参数:-Xms5m -Xmx5m -XX:+PrintGCDetails)

 //-Xms5m -Xmx5m -XX:+PrintGCDetailspublic class SoftReferenceTest {public static void main(String[] args) {int[] list = new int[10];SoftReference listSoftReference = new SoftReference(list);list = null;​//[I@61bbe9baSystem.out.println(listSoftReference.get());​//模拟空间资源不足try{byte[] bytes = new byte[1024 * 1024 * 4];System.gc();}catch (Exception e){e.printStackTrace();}finally {//nullSystem.out.println(listSoftReference.get());}}}
弱引用

无论内存是否足够,当发生GC时都会对弱引用进行回收

弱引用使用WeakReference

内存充足情况下的弱引用

     public static void test1() {WeakReference<int[]> weakReference = new WeakReference<>(new int[1]);//[I@511d50c0System.out.println(weakReference.get());​System.gc();//nullSystem.out.println(weakReference.get());}

WeakHashMap

JDK中有一个WeakHashMap,使用与Map相同,只不过节点为弱引用

image-20210504212605513.png

当key的引用不存在引用的情况下,发生GC时,WeakHashMap中该键值对就会被删除

     public static void test2() {WeakHashMap<String, String> weakHashMap = new WeakHashMap<>();HashMap<String, String> hashMap = new HashMap<>();​String s1 = new String("3.jpg");String s2 = new String("4.jpg");​hashMap.put(s1, "图片1");hashMap.put(s2, "图片2");weakHashMap.put(s1, "图片1");weakHashMap.put(s2, "图片2");​//只将s1赋值为空时,堆中的3.jpg字符串还会存在强引用,所以要removehashMap.remove(s1);s1=null;s2=null;​System.gc();​//4.jpg=图片2test2Iteration(hashMap);​//4.jpg=图片2test2Iteration(weakHashMap);}​private static void test2Iteration(Map<String, String>  map){Iterator iterator = map.entrySet().iterator();while (iterator.hasNext()){Map.Entry entry = (Map.Entry) iterator.next();System.out.println(entry);}}

未显示删除weakHashMap中的该key,当这个key没有其他地方引用时就删除该键值对

软引用,弱引用适用的场景

数据量很大占用内存过多可能造成内存溢出的场景

比如需要加载大量数据,全部加载到内存中可能造成内存溢出,就可以使用软引用、弱引用来充当缓存,当内存不足时,JVM对这些数据进行回收

使用软引用时,可以自定义Map进行存储Map<String,SoftReference<XXX>> cache

使用弱引用时,则可以直接使用WeakHashMap

软引用与弱引用的区别则是GC回收的时机不同,软引用存活可能更久,Full GC下才回收;而弱引用存活可能更短,发生GC就会回收

虚引用

使用PhantomReference创建虚引用,需要搭配引用队列ReferenceQueue使用

无法通过虚引用得到该对象实例(其他引用都可以得到实例)

虚引用只是为了能在这个对象被收集器回收时收到一个通知

引用队列搭配虚引用使用

 public class PhantomReferenceTest {private static PhantomReferenceTest reference;private static ReferenceQueue queue;​@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("调用finalize方法");//搭上引用链reference = this;}​public static void main(String[] args) {reference = new PhantomReferenceTest();//引用队列queue = new ReferenceQueue<>();//虚引用PhantomReference<PhantomReferenceTest> phantomReference = new PhantomReference<>(reference, queue);Thread thread = new Thread(() -> {PhantomReference<PhantomReferenceTest> r = null;while (true) {if (queue != null) {r = (PhantomReference<PhantomReferenceTest>) queue.poll();//说明被回收了,得到通知if (r != null) {System.out.println("实例被回收");}}}});thread.setDaemon(true);thread.start();​​//null (获取不到虚引用)System.out.println(phantomReference.get());​try {System.out.println("第一次gc 对象可以复活");reference = null;//第一次GC 引用不可达 守护线程执行finalize方法 重新变为可达对象System.gc();TimeUnit.SECONDS.sleep(1);if (reference == null) {System.out.println("object is dead");} else {System.out.println("object is alive");}reference = null;System.out.println("第二次gc 对象死了");//第二次GC 不会执行finalize方法 不能再变为可达对象System.gc();TimeUnit.SECONDS.sleep(1);if (reference == null) {System.out.println("object is dead");} else {System.out.println("object is alive");}} catch (InterruptedException e) {e.printStackTrace();}​}}

结果:

 /*null第一次gc 对象可以复活调用finalize方法object is alive第二次gc 对象死了实例被回收object is dead*/

第一次GC时,守护线程执行finalize方法让虚引用重新可达,所以没死

第二次GC时,不再执行finalize方法,虚引用已死

虚引用回收后,引用队列有数据,来通知告诉我们reference这个对象被回收了

使用场景

GC只能回收堆内内存,而直接内存GC是无法回收的,直接内存代表的对象创建一个虚引用,加入引用队列,当这个直接内存不使用,这个代表直接内存的对象为空时,这个虚内存就死了,然后引用队列会产生通知,就可以通知JVM去回收堆外内存(直接内存)

总结

本篇文章围绕引用深入浅出的解析内存溢出与泄漏、强引用、软引用、弱引用、虚引用

当JVM没有足够的内存为新对象分配空间时就会发生内存溢出抛出OOM

内存溢出有两种情况,一种是分配的资源太少,不满足必要对象的内存;另一种是发生内存泄漏,不合理的设置对象的生命周期、不关闭资源都会导致内存泄漏

使用最常见的就是强引用,强引用只有在可达性分析算法中不可达时才会回收,强引用使用不当是造成内存泄漏的原因之一

使用SoftReference软引用时,只要内存不足触发Full GC时就会对软引用进行回收

使用WeakReference弱引用时,只要发生GC就会对弱引用进行回收

软、弱引用可以用来充当大数据情况下的缓存,它们的区别就是软引用可能活的更久Full GC才回收,使用弱引用时可以直接使用JDK中提供的WeakHashMap

虚引用无法在程序中获取,与引用队列搭配使用,当虚引用被回收时,能够从引用队列中取出(感知),可以在直接引用不使用时,发出消息让JVM进行回收

最后(一键三连求求拉~)

本篇文章将被收入JVM专栏,觉得不错感兴趣的同学可以收藏专栏哟~

本篇文章笔记以及案例被收入 gitee-StudyJava、 github-StudyJava 感兴趣的同学可以stat下持续关注喔~

有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~

关注菜菜,分享更多干货,公众号:菜菜的后端私房菜

本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2812994.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

索引学习以及索引原理

有时候&#xff0c;建索引并不一定会加快查询效率。但是&#xff0c;有时候&#xff0c;表的数据量是大数据量的话&#xff0c;还是要看下是否能使用索引优化查询效率。 1、建索引的几大原则&#xff1a; 1.1、最左前缀匹配原则非常重要的原则&#xff0c;mysql会一直向右匹配…

计算机设计大赛 深度学习大数据物流平台 python

文章目录 0 前言1 课题背景2 物流大数据平台的架构与设计3 智能车货匹配推荐算法的实现**1\. 问题陈述****2\. 算法模型**3\. 模型构建总览 **4 司机标签体系的搭建及算法****1\. 冷启动**2\. LSTM多标签模型算法 5 货运价格预测6 总结7 部分核心代码8 最后 0 前言 &#x1f5…

时间序列分析实战(四):Holt-Winters建模及预测

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

C# paddlerocrsharp识别身份证号

https://gitee.com/raoyutian/paddle-ocrsharp 项目搭建 新建控制台项目 安装paddleocrsharp 下载训练好的模型 解压放到对应的文件夹中&#xff0c;都修改为如果较新则复制 编写代码OCRHelper.cs using PaddleOCRSharp;namespace OCRTest02;public class OCRHelper {//…

推荐10款C#开源好用的Windows软件

DevToys 项目简介&#xff1a;DevToys是一个专门为开发者设计的Windows工具箱&#xff0c;完全支持离线运行&#xff0c;无需使用许多不真实的网站来处理你的数据&#xff0c;常用功能有&#xff1a;格式化&#xff08;支持 JSON、SQL、XML&#xff09;、JWT解码、URL编码/解码…

NOIP2018-J-4-对称二叉树的题解

原题描述&#xff1a; 题目描述 时间&#xff1a;1s 空间&#xff1a;256M 一棵有点权的有根树如果满足以下条件&#xff0c;则被轩轩称为对称二叉树&#xff1a; 1. 二叉树&#xff1b; 2. 将这棵树所有节点的左右子树交换&#xff0c;新树和原树对应位置的结构相同且…

【SD Card-电路】

SD Card-电路 ■ SD Card■ SD Card&#xff08;DOCK&#xff09;■ ■ SD Card ■ SD Card&#xff08;DOCK&#xff09; A_SD_DATA0 等IO之间连接芯片imax6u ■

【数据结构】队列OJ题《用队列实现栈》(题库+解析+代码)

1.前言 通过前面队列的实现和详解大家对队列应该有一定熟悉了&#xff0c;现在上强度开始做题吧 队列详解&#xff1a;http://t.csdnimg.cn/dvTsW 2.OJ题目训练225. 用队列实现栈 题目分析 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0…

Springboot 多级缓存设计与实现

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&…

Python算法题集_子集

Python算法题集_子集 题78&#xff1a;子集1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【递归】2) 改进版一【双层下标循环】3) 改进版二【双层枚举循环】4) 改进版三【双层下标循环位运算】5) 改进版四【单行双层循环位运算】6) 改进版五【…

算法沉淀——动态规划之子数组、子串系列(上)(leetcode真题剖析)

算法沉淀——动态规划之子数组、子串系列 01.最大子数组和02.环形子数组的最大和03.乘积最大子数组04.乘积为正数的最长子数组长度 01.最大子数组和 题目链接&#xff1a;https://leetcode.cn/problems/maximum-subarray/、 给你一个整数数组 nums &#xff0c;请你找出一个具…

golang学习6,glang的web的restful接口传参

1.get传参 //get请求 返回json 接口传参r.GET("/getJson/:id", controller.GetUserInfo) 1.2.接收处理 package controllerimport "github.com/gin-gonic/gin"func GetUserInfo(c *gin.Context) {_ c.Param("id")ReturnSucess(c, 200, &quo…

DSP,QX320F280049C,完整版使用手册,数据手册

32位双核CPU&#xff0c; 主频150MHz 支持FPU、VCU、TPU flash 1MB SRAM 500KB 3个3MSPS&#xff0c;12位 ADC 24个增强型ePWM 16个高分辨率HRPWM&#xff08;150PS&#xff09;

顶顶通呼叫中心中间件-如何使处于机器人话术中的通话手动转接到坐席分机上讲解(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件使用httpapi实现电话转接操作过程讲解(mod_cti基于FreeSWITCH) 需要了解呼叫中心中间件可以点以下链接了解顶顶通小孙 1、使用httpapi接口转接 一、打开web版的ccadmin并且找到接口测试 打开web-ccadmin并且登录&#xff0c;登录完成之后点击运维调试-再…

Linux使用Docker部署在线协作白板WBO并结合内网穿透发布公网远程访问

文章目录 前言1. 部署WBO白板2. 本地访问WBO白板3. Linux 安装cpolar4. 配置WBO公网访问地址5. 公网远程访问WBO白板6. 固定WBO白板公网地址 前言 WBO在线协作白板是一个自由和开源的在线协作白板&#xff0c;允许多个用户同时在一个虚拟的大型白板上画图。该白板对所有线上用…

【JavaEE】_前端POST请求借助form表单向后端传参

对于POST请求&#xff0c;可以通过body部分来传递参数&#xff1b; 对于通过form表单的方式将POST请求的参数传递给后端来说&#xff0c;body部分的格式就是query string的格式&#xff0c;即form表单&#xff1b; 此时请求报头部分有&#xff1a;Content-Type : application…

spring框架Bean的作用域?对需要保持会话状态的bean应使用prototype作用域?为啥?

当一个bean被定义为"prototype"作用域时&#xff0c;每次请求该bean时都会创建一个新的实例&#xff0c;而不是像"singleton"作用域那样共享同一个实例。 对于需要保持会话状态的bean&#xff0c;如果使用"singleton"作用域&#xff0c;会导致所…

MATLAB中的稀疏矩阵和密集矩阵

在MATLAB中&#xff0c;矩阵可以表示为密集或稀疏格式。通常&#xff0c;矩阵默认以密集格式存储&#xff0c;这意味着每个元素都明确地存储在内存中&#xff0c;无论它的值是多少。然而&#xff0c;当矩阵含有大量的零元素时&#xff0c;这种存储方式就会变得非常低效。为了更…

Window系统本地搭建LightPicture网站并实现远程上传下载本地图片

文章目录 1.前言2. Lightpicture网站搭建2.1. Lightpicture下载和安装2.2. Lightpicture网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 现在的手机越来越先进&#xff0c;功能也越来越多&#xff0c;而手机…

后端程序员入门react笔记(五)ajax请求

常见的ajax Ajax 最原始的方式&#xff0c;基于原生的js XmlHttpRequest 多个请求之间如果有先后关系&#xff0c;会存在很多层回调的问题&#xff0c;也是基于原生js Jquery Ajax 基于原生XHR封装&#xff0c;依赖Jquery框架&#xff0c;由jquery 框架去封装原生的XML(Xml)封…