容易发生内存泄漏的八个场景,你都知道吗?
内存泄漏与内存溢出
JVM在运行时会存在大量的对象,一部分对象是长久使用的,一部分对象只会短暂使用
JVM会通过可达性分析算法和一些条件判断对象是否再使用,当对象不再使用时,通过GC将这些对象进行回收,避免资源被用尽
内存泄漏:当不再需要使用的对象,因为不正确使用时,可能导致GC无法回收这些对象
当不正确的使用导致对象生命周期变成也是宽泛意义上的内存泄漏
内存溢出:当大量内存泄漏时,可能没有资源为新对象分配
举例内存泄漏
接下来将从对象生命周期变长、不关闭资源、改变对象哈希值、缓存等多个场景举例内存泄漏
对象生命周期变长引发内存泄漏
静态集合类
public class StaticClass {private static final List<Object> list = new ArrayList<>();/*** 尽管这个局部变量Object生命周期非常短* 但是它被生命周期非常长的静态列表引用* 所以不会被GC回收 发生内存溢出*/public void addObject(){Object o = new Object();list.add(o);}
}
类卸载的条件非常苛刻,这个静态列表生命周期基本与JVM一样长
静态集合引用局部对象,使得局部对象生命周期变长,发生内存泄漏
饿汉式单例模式
public class Singleton {private static final Singleton INSTANCE = new Singleton();private Singleton(){if (INSTANCE!=null){throw new RuntimeException("not create instance");}}public static Singleton getInstance(){return INSTANCE;}
}
饿汉式的单例模式也是被静态变量引用,即时不需要使用这个单例对象,GC也不会回收
非静态内部类
非静态内部类会有一个指针指向外部类
public class InnerClassTest {class InnerClass {}public InnerClass getInnerInstance() {return this.new InnerClass();}public static void main(String[] args) {InnerClass innerInstance = null;{InnerClassTest innerClassTest = new InnerClassTest();innerInstance = innerClassTest.getInnerInstance();System.out.println("===================外部实例对象内存布局==========================");System.out.println(ClassLayout.parseInstance(innerClassTest).toPrintable());System.out.println("===================内部实例对象内存布局===========================");System.out.println(ClassLayout.parseInstance(innerInstance).toPrintable());}//省略很多代码.....}
}
当调用外部类实例方法通过外部实例对象返回一个内部实例对象时(调用代码中的getInnerInstance方法)
外部实例对象不需要使用了,但内部实例对象被长期使用,会导致这个外部实例对象生命周期变长
因为内部实例对象隐藏了一个指针指向(引用)创建它的外部实例对象
实例变量作用域不合理
如果只需要一个变量作为局部变量,在方法结束就不使用它了,但是把他设置为实例变量,此时如果该类的实例对象生命周期很长也会导致该变量无法回收发生内存泄漏(因为实例对象引用了它)
变量作用域设置的不合理会导致内存泄漏
隐式内存泄漏
动态数组ArrayList中remove操作会改变size的同时将删除位置置空,从而不再引用元素,避免内存泄漏
不置空要删除的元素对数组的添加删除查询等操作毫无影响(看起来是正常的),只是会带来隐式内存泄漏
不关闭资源引发内存泄漏
各种连接: 数据库连接、网络连接、IO连接在使用后忘记关闭,GC无法回收它们,会发生内存泄漏
所以使用连接时要使用 try-with-resource
自动关闭连接
改变对象哈希值引发内存泄漏
一般认为对象逻辑相等,只要对象关键域相等即可
一个对象加入到散列表是通过计算该对象的哈希值,通过哈希算法得到放入到散列表哪个索引中
如果将对象存入散列表后,修改了该对象的关键域,就会改变对象哈希值,导致后续要在散列表中删除该对象,会找错索引从而找不到该对象导致删除失败(极小概率找得到)
public class HashCodeTest {/*** 假设该对象实例变量a,d是关键域* a,d分别相等的对象逻辑相等*/private int a;private double d;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;HashCodeTest that = (HashCodeTest) o;return a == that.a &&Double.compare(that.d, d) == 0;}@Overridepublic int hashCode() {return Objects.hash(a, d);}public HashCodeTest(int a, double d) {this.a = a;this.d = d;}public HashCodeTest() {}@Overridepublic String toString() {return "HashCodeTest{" +"a=" + a +", d=" + d +'}';}public static void main(String[] args) {HashMap<HashCodeTest, Integer> map = new HashMap<>();HashCodeTest h1 = new HashCodeTest(1, 1.5);map.put(h1, 100);map.put(new HashCodeTest(2, 2.5), 200);//修改关键域 导致改变哈希值h1.a=100;System.out.println(map.remove(h1));//nullSet<Map.Entry<HashCodeTest, Integer>> entrySet = map.entrySet();for (Map.Entry<HashCodeTest, Integer> entry : entrySet) {System.out.println(entry);}//HashCodeTest{a=100, d=1.5}=100//HashCodeTest{a=2, d=2.5}=200}
}
所以说对象当作Key存入散列表时,该对象最好是逻辑不可变对象,不能在外界改变它的关键域,从而无法改变哈希值
将关键域设置为final,只能在实例代码块中初始化或构造器中
如果关键域是引用类型,可以用final修饰后,对外不提供改变该引用关键域的方法,从而让外界无法修改引用关键域中的值 (如同String类型,所以String常常用来当作散列表的Key)
缓存引发内存泄漏
当缓存充当散列表的Key时,如果不再使用该缓存,就要手动在散列表中删除,否则会发生内存泄漏
如果使用的是WeakHashMap,它内部的Entry是弱引用,当它的Key不再使用时,下次垃圾回收就会回收掉,不会发生内存泄漏
public class CacheTest {private static Map<String, String> weakHashMap = new WeakHashMap<>();private static Map<String, String> map = new HashMap<>();public static void main(String[] args) {//模拟要缓存的对象String s1 = new String("O1");String s2 = new String("O2");weakHashMap.put(s1,"S1");map.put(s2,"S2");//模拟不再使用缓存s1=null;s2=null;//垃圾回收WeakHashMap中存的弱引用System.gc();try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}//遍历各个散列表System.out.println("============HashMap===========");traverseMaps(map);System.out.println();System.out.println("============WeakHashMap===========");traverseMaps(weakHashMap);}private static void traverseMaps(Map<String, String> map){for (Map.Entry<String, String> entry : map.entrySet()) {System.out.println(entry);}}
}
结果
注意: 监听器和回调 也应该像这样成为弱引用
总结
这篇文章介绍内存泄漏与内存溢出的区别,并从生命周期变长、不关闭资源、改变哈希值、缓存等多方面举例内存泄漏的场景
内存泄漏是指当对象不再使用,但是GC无法回收该对象
内存溢出是指当大量对象内存泄漏,没有资源再给新对象分配
静态集合、饿汉单例、不合理的设置变量作用域都会使对象生命周期变长,从而导致内存泄漏
非静态内部对象有隐式指向外部对象的指针、使用集合不删除元素等都会隐式导致内存泄漏
忘记关闭资源导致内存泄漏(try-with-resource自动关闭解决)
使用散列表时,充当Key 对象的哈希值被改变导致内存泄漏(key 使用逻辑不可变对象,关键域不能被修改)
缓存引发内存泄漏(使用弱引用解决)
最后(一键三连求求拉~)
本篇文章将被收入JVM专栏,觉得不错感兴趣的同学可以收藏专栏哟~
本篇文章笔记以及案例被收入 gitee-StudyJava、 github-StudyJava 感兴趣的同学可以stat下持续关注喔~
有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~
关注菜菜,分享更多干货,公众号:菜菜的后端私房菜
本文由博客一文多发平台 OpenWrite 发布!
相关文章:

容易发生内存泄漏的八个场景,你都知道吗?
内存泄漏与内存溢出 JVM在运行时会存在大量的对象,一部分对象是长久使用的,一部分对象只会短暂使用 JVM会通过可达性分析算法和一些条件判断对象是否再使用,当对象不再使用时,通过GC将这些对象进行回收,避免资源被用…...
掌握 Vue3 中的 setup 函数
Vue.js 经历了从 Vue 2 到 Vue 3 的重大变革,带来了许多引人注目的新特性和性能优化。其中,setup函数无疑是最引人瞩目的新星之一。 一、概览 setup函数是 Vue 3 引入的一个新的组件选项,作为组合式 API 中心,它允许开发者在一个…...

BUUCTF AWD-Test1
打开靶场是这个有些简陋的界面。 随便点点,找到这个东西。 看到ThinkPHP,思路瞬间清晰,老熟人了。这个就是ThinkPHP漏洞。根据版本我们去找一下poc。 /index.php/?sIndex/\think\View/display&content%22%3C?%3E%3C?php%20phpinfo();…...

百亿诈骗案频出,欧科云链用“技术责任”拓宽Web3安全边界
2022年12月1日,《中华人民共和国反电信网络诈骗法》正式实施,中国正式迈入“全民反诈时代”。据CNNIC和智研咨询统计显示,截至2021年12月,国内网民遭遇网络诈骗比例为16.6%,数千万人深受网络诈骗的危害。 以新兴技术区…...

一个实时波形图的封装demo(QT)(qcustomplot)
前言: 封装的一个实时波形图的类,可以直接提升使用。 提供了接口,可以更改颜色,样式,等等 参考: Qt Plotting Widget QCustomPlot - Introduction 另外参考了一个大神的作品,链接没找到。 项目…...

Java进阶-反射
来学习一下Java的反射,通过Class实例获取class信息的方法称为反射(Reflection),内容如下 一、反射机制 1、概述 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一…...

力扣180 连续出现的数字
如何有效地识别在数据库中至少连续出现三次的数字? 目录 题目描述 解题思路 完整代码 进一步探索 题目描述 表:Logs ---------------------- | Column Name | Type | ---------------------- | id | int | | num | varch…...
C++面试 -操作系统-架构能力:内存问题分析与性能优化
内存问题分析: 内存泄漏: 描述什么是内存泄漏,以及它如何在 C 中发生。使用工具(如 Valgrind、AddressSanitizer)来检测和定位内存泄漏。如何预防内存泄漏?使用智能指针、正确释放资源等。 野指针和悬挂指针…...

基于springboot+vue的共享汽车管理系统(前后端分离)
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(Javaweb项目|小程序|Pyt…...
All Roads Lead to Rome (30)
1、题目: Indeed there are many different tourist routes from our city to Rome. You are supposed to find your clients the route with the least cost while gaining the most happiness. Input Specification: Each input file contains one test case. For…...
GO语言学习笔记(与Java的比较学习)(四)
结构体 一个结构体(struct)就是一组字段(field)。 package main import "fmt" type Vertex struct {X intY int } func main() {fmt.Println(Vertex{1, 2}) } 结构体中的字段用 . 访问 package main im…...

在实训云平台上配置云主机
文章目录 零、学习目标一、实训云升级二、实训云登录(一)登录实训云(二)切换界面语言(三)规划云主机实例 三、创建网络三、创建路由器2024-2-29更新到此四、添加接口五、创建端口六、添加安全组规则七、创建…...

什么是隔离式栅极驱动器?
在电子设备领域,“驱动”一词占据着至关重要的地位,充当推动信号、控制和电源的力量。这个复杂世界中的一个重要组件是隔离式栅极驱动器,这项技术在确保各种电子系统高效、安全运行方面发挥着关键作用。 什么是栅极驱动器? 从本质…...

蓝桥杯算法赛 第 6 场 小白入门赛 解题报告 | 珂学家 | 简单场 + 元宵节日快乐
前言 整体评价 因为适逢元宵节,所以这场以娱乐为主。 A. 元宵节快乐 题型: 签到 节日快乐,出题人也说出来自己的心愿, 祝大家AK快乐! import java.util.Scanner;public class Main {public static void main(String[] args) {System.out.println(&qu…...

附加Numpy数组
参考:Append Numpy Array 引言 在数据科学和机器学习领域,处理大规模数据集是一项重要且常见的任务。为了高效地处理数据,numpy是一个非常强大的Python库。本文将详细介绍numpy中的一个重要操作,即如何附加(append&a…...

收银系统源码-智慧新零售,ERP进销存功能详解
智慧新零售是一套线下线上一体化的收银系统,不仅给门店线下提供了多样化的收款方式,还提供了和线下深度打通的线上小程序商城。有线下又有线上自然需要一套完整的进销存模块能高效的管理商品。 智慧新零售进销存功能涵盖了商品的采购、销售、调拨、盘点…...
STM32使用PB3, PB4引脚的注意事项
STM32的PB3, PB4引脚作为GPIO引脚需要注意,因为他们默认分别是JTDO和NJTRST引脚。 笔者在设计可调增益增益放大器(VGA)的时候,使用4个GPIO读取外部控制电压,根据约定的编码格式设定DAC的输出电压,从而设置V…...

OSCP靶场--DVR4
OSCP靶场–DVR4 考点(1.windows:路径遍历获取私钥getshell 2.ssh shell中runas切换用户) 1.nmap扫描 ┌──(root㉿kali)-[~/Desktop] └─# nmap -sV -sC -p- 192.168.161.179 --min-rate 2000 Starting Nmap 7.92 ( https://nmap.org ) at 2024-02-29 07:14 EST…...

【嵌入式——QT】日期与定时器
日期 QTime:时间数据类型,仅表示时间,如 16:16:16;QDate:日期数据类型,仅表示日期,如2024-1-22;QDateTime:日期时间数据类型,表示日期和时间,如2…...
如何决定使用HashMap还是TreeMap?
使用 HashMap 还是 TreeMap 取决于你的需求和对数据结构的理解。 HashMap: 快速插入和查找:HashMap 提供了常数时间(O(1))的插入和查找操作,因此对于需要快速插入和查找的场景非常适用。无序性:HashMap 不保…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
Python常用模块:time、os、shutil与flask初探
一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...

Centos 7 服务器部署多网站
一、准备工作 安装 Apache bash sudo yum install httpd -y sudo systemctl start httpd sudo systemctl enable httpd创建网站目录 假设部署 2 个网站,目录结构如下: bash sudo mkdir -p /var/www/site1/html sudo mkdir -p /var/www/site2/html添加测试…...

数据可视化交互
目录 【实验目的】 【实验原理】 【实验环境】 【实验步骤】 一、安装 pyecharts 二、下载数据 三、实验任务 实验 1:AQI 横向对比条形图 代码说明: 运行结果: 实验 2:AQI 等级分布饼图 实验 3:多城市 AQI…...