内存溢出与内存泄漏详解!

文章目录

  • 引言
  • 内存溢出
    • 导致的原因
    • 堆内存溢出
    • 栈内存溢出
    • 永久代/元空间溢出
    • 解决方法
  • 内存泄漏
    • 导致原因
    • 静态集合类导致的内存泄漏
    • 监听器和回调未被移除
    • 长生命周期对象持有短生命周期对象
    • 解决方法
    • 示例代码
  • 对比
  • 总结

引言

内存溢出和内存泄漏是我们经常听到的两种内存管理问题,那么,它们是如何导致的?又该如何解决?这篇文章,我们来聊一聊。

内存溢出

内存溢出(OutOfMemoryError)是指程序在运行时尝试分配内存,但由于没有足够的内存可用,Java 虚拟机(JVM)抛出了 OutOfMemoryError 错误。常见的内存溢出区域包括堆内存和永久代(在 Java 8 之后被元空间取代)。

导致的原因

1、堆内存溢出:导致堆内存耗尽
2、栈内存溢出:递归调用过深,导致栈内存耗尽
3、永久代/元空间溢出:类加载过多,导致永久代/元空间耗尽。

下面我们用三个示例,分别展示了堆内存溢出、栈内存溢出和永久代/元空间溢出的情况:

堆内存溢出

如下示例代码,通过不断向 ArrayList 添加对象来耗尽堆内存。

import java.util.ArrayList;
import java.util.List;public class HeapMemoryOverflow {public static void main(String[] args) {List<Object> list = new ArrayList<>();while (true) {list.add(new Object());}}
}

在运行上述 HeapMemoryOverflow 示例时,可能需要调整 JVM 参数以较小的堆大小运行,例如 -Xmx10m,以更快地观察到 OutOfMemoryError。

栈内存溢出

如下示例代码,通过递归调用一个没有终止条件的方法,导致栈内存溢出。

public class StackMemoryOverflow {public static void main(String[] args) {recursiveMethod();}public static void recursiveMethod() {// 没有终止条件的递归调用recursiveMethod();}
}

运行StackOverflowError代码,通常会很快发生栈内存溢出,因为默认的栈大小不大。

永久代/元空间溢出

在 Java 8 之前,永久代溢出可以通过动态生成大量类来模拟,Java 8 之后,永久代被元空间取代,以下是一个使用 CGLIB 动态生成类的示例,可能导致元空间溢出,需要添加 CGLIB 库依赖。

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class MetaspaceOverflow {public static void main(String[] args) {while (true) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(DummyClass.class);enhancer.setUseCache(false);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {return proxy.invokeSuper(obj, args);}});enhancer.create();}}static class DummyClass {}
}

运行 MetaspaceOverflow 示例时,可以使用 JVM 参数 -XX:MaxMetaspaceSize=10m 来限制元空间大小,以更快地观察到溢出。

解决方法

1、增加内存:调整 JVM 参数增加堆内存大小,如 -Xmx。
2、优化代码:减少不必要的对象创建,优化数据结构。
3、检查递归:避免过深的递归调用。
4、监控和分析:使用工具如 JVisualVM、JProfiler 分析内存使用情况


内存泄漏

内存泄漏(Memory Leak)是指程序中存在一些对象,它们不再被使用,但由于仍然被引用,垃圾回收器无法回收这些对象。因此,随着时间的推移,内存泄漏会导致可用内存逐渐减少,最终可能导致内存溢出。

导致原因

1、静态集合类:使用 static 修饰的集合类持有对象引用,因为静态集合的生命周期和 JVM 一致,所以静态集合引用的对象不能被释放。
2、监听器和回调:注册的监听器或回调未被移除。
3、长生命周期对象持有短生命周期对象:长生命周期对象不当持有短生命周期对象的引用。

下面我们用三个示例,分别展示了内存泄漏可能发生的场景:

静态集合类导致的内存泄漏

静态集合类持有对象引用,导致这些对象无法被垃圾回收。

import java.util.ArrayList;
import java.util.List;public class StaticCollectionLeak {// 静态集合持有对象引用private static List<Object> objectList = new ArrayList<>();public static void main(String[] args) {for (int i = 0; i < 10000; i++) {// 每次创建一个新对象并添加到静态集合中objectList.add(new Object());}// 即使在这里试图清理掉一些其他的引用System.gc();  // 这些对象仍然无法被回收,因为它们被静态集合引用}
}

监听器和回调未被移除

注册的监听器或回调未被移除,导致内存泄漏。

import java.util.ArrayList;
import java.util.List;public class ListenerLeak {private List<EventListener> listeners = new ArrayList<>();public void addListener(EventListener listener) {listeners.add(listener);}public void triggerEvent() {for (EventListener listener : listeners) {listener.onEvent();}}public static void main(String[] args) {ListenerLeak leakExample = new ListenerLeak();// 匿名类创建的监听器对象leakExample.addListener(new EventListener() {@Overridepublic void onEvent() {System.out.println("Event triggered");}});// 假设在某个时候不再需要监听器,但未移除// listeners.remove(listener); // 应该移除不需要的监听器}
}interface EventListener {void onEvent();
}

长生命周期对象持有短生命周期对象

长生命周期对象不当持有短生命周期对象的引用,导致短生命周期对象无法被回收。

import java.util.HashMap;
import java.util.Map;public class LongLifeCycleLeak {private static Map<String, byte[]> cache = new HashMap<>();public static void main(String[] args) {while (true) {// 短生命周期对象byte[] data = new byte[1024 * 1024]; // 1MB// 长生命周期对象持有短生命周期对象的引用cache.put(String.valueOf(System.nanoTime()), data);// 需要定期移除不再需要的数据,否则会导致内存泄漏// cache.clear(); // 应该在适当时机清理缓存}}
}

解决方法

1、及时释放引用:确保不再使用的对象引用被清除。
2、使用弱引用:对缓存或非关键对象使用 WeakReference。比如 ThreadLocal 的弱引用会导致内存泄漏,因此使用完 ThreadLocal 一定要记得使用 remove 方法来进行清除。
3、正确管理生命周期:特别是监听器和回调,确保在不需要时移除。

示例代码

下面示例代码,用于测试内存泄漏。

import java.util.HashMap;
import java.util.Map;public class MemoryLeakExample {private static Map<Integer, String> map = new HashMap<>();public static void main(String[] args) {for (int i = 0; i < 100000; i++) {map.put(i, "value" + i);}}
}

在上面的代码中,如果 map 是一个长期存在的静态变量,并且没有及时清理,则可能导致内存泄漏。


对比

关于内存溢出和内存泄漏的比较如下:

1、触发时机:内存溢出通常在内存耗尽时立即触发,而内存泄漏可能在一段时间后逐渐显现。
2、影响范围:内存溢出会立即影响程序的可用性,而内存泄漏通常是一个逐步积累的问题。
3、检测难度:内存溢出较容易检测,而内存泄漏往往需要深入分析和调试。
4、解决复杂度:内存溢出的解决相对简单,通常通过优化内存使用或增加内存即可。而内存泄漏的解决需要识别并清理不必要的引用,可能涉及更复杂的代码重构。

总结

本文,我们分析了Java的内存溢出和内存泄漏并且应示例展示了它们导致的原因,应该说它们是比较常见的内存管理问题,如果在生产环境出现也是比较头疼的问题。所以在日常开发中,我们一定要注意自己的代码风格和代码质量,尽量避免这些问题的发生。

编辑:三两肉
来源:猿Java

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA采集adc,IP核用法,AD驱动(上半部分)

未完结&#xff0c;明天补全 IP核&#xff1a;集成的一个现有的模块 串口写好后基本不会再修改串口模块内部的一些逻辑&#xff0c;将串口.v文件添加进来&#xff0c;之后通过他的上层的接口去对他进行使用&#xff0c;所以我们打包IP&#xff0c;之后就不用去添加源文件了&a…

无人机和鸟数据集,无人机数据集+鸟数据集 yolo格式,可以直接用于模型的训练。7000张,图片自己打的标签 yolov5-yolov10通用

无人机和鸟数据集&#xff0c;无人机数据集鸟数据集 yolo格式&#xff0c;可以直接用于模型的训练。7000张&#xff0c;图片自己打的标签 yolov5-yolov10通用 无人机及鸟类目标检测数据集规模&#xff1a; 总图像数量&#xff1a;约7,000张类别&#xff1a;2类检测目标 Drone&…

从一个简单的计算问题,看国内几个大语言模型推理逻辑能力

引言 首先&#xff0c;来看问题&#xff1a; 123456*987654等于多少&#xff0c;给出你计算的过程。 从openai推出chatgpt以来&#xff0c;大模型发展的很快&#xff0c;笔者也经常使用免费的大语言模型辅助进行文档编写和编码工作。大模型推出时间也好久了&#xff0c;笔者想…

【独家:AI编程助手Cursor如何revolutionize Java设计模式学习】

【独家:AI编程助手Cursor如何revolutionize Java设计模式学习】 导语 在Java高级编程的世界里,设计模式是每个开发者必须掌握的利器。但是,如何快速理解并灵活运用这些模式呢?让我们一起探索如何借助AI编程助手Cursor,轻松掌握设计模式,提升Java编程技能! 正文 设计模式:J…

易控天地|易控天地标准版3.0(EconTNT STD3.0)安装记录

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 以前使用过的组态软件WinCC、杰控、MCGS、组态王、KingSCADA、KingFunsion等&#xff0c; 关于易控天地去年在现场见到过&#xff0c;接下来安装体验下易控天地&#xff1b; 以下为安装笔记。 01 解压缩 下载完安装…

【YOLO模型】(1)--YOLO是什么

一、什么是YOLO YOLO&#xff08;You Only Look Once&#xff09;是一种基于深度学习的目标检测算法&#xff0c;由Joseph Redmon等人于2016年提出。 1. 核心思想 它的核心思想是将目标检测问题转化为一个回归问题&#xff0c;通过一个神经网络直接预测目标的类别和位置。 …

[Linux] CentOS7替换yum源为阿里云并安装gcc详细过程(附下载链接)

前言 CentOS7替换yum源为阿里云 yum是CentOS中的一种软件管理器&#xff0c;通过yum安装软件&#xff0c;可以自动解决包依赖的问题&#xff0c;免去手工安装依赖包的麻烦。 yum使用了一个中心仓库来记录和管理软件的依赖关系&#xff0c;默认为mirrorlist.centos.org&#xf…

1208. 尽可能使字符串相等

Problem: 1208. 尽可能使字符串相等 题目描述 给定两个相同长度的字符串 s 和 t&#xff0c;将字符串 s 转换为字符串 t 需要消耗开销&#xff0c;开销是两个字符的 ASCII 码差值的绝对值。还有一个最大预算 maxCost&#xff0c;我们需要在这个预算范围内&#xff0c;找到 s 中…

时钟分频电路之Innovus自动产生的_clock_gen skew group盘点

我们在查看时钟树综合的log时会发现工具会自动生成一些skew group&#xff0c;这些skew group的名字都是以_clock_gen开头的。 skew_group _clock_gen_CLK_CORE_PLL_clk_reg_1/func: insertion delay [min0.020, max0.064, avg0.038, sd0.022], skew [0.045 vs 0.050], 100% {…

SSL证书有免费的吗?在哪里可以申请到?——附带申请步骤

申请免费的SSL证书通常可以通过以下几个步骤完成&#xff0c;这里以使用JoySSL为例进行说明&#xff0c;因为JoySSL提供了一个免费、自动化和开放的证书颁发机构&#xff08;CA&#xff09;来促进网站从HTTP向HTTPS的转换。 步骤&#xff1a; 选择工具&#xff1a; 访问JoySSL…

二百六十八、Kettle——同步ClickHouse清洗数据到Hive的DWD层静态分区表中(每天一次)

一、目的 实时数仓用的是ClickHouse&#xff0c;为了避免Hive还要清洗数据&#xff0c;因此就直接把ClickHouse中清洗数据同步到Hive中就行 二、所需工具 ClickHouse&#xff1a;clickhouse-client-21.9.5.16 Kettle&#xff1a;kettle9.2 Hadoop&#xff1a;hadoop-3.1.3…

汽车免拆诊断案例 | 2019 款奥迪 A6L 车行驶中偶发熄火

故障现象  一辆2019款奥迪A6L车&#xff0c;搭载2.0T发动机&#xff0c;累计行驶里程约为9万km。车主反映&#xff0c;车辆行驶中偶发熄火&#xff0c;故障频率较高。 故障诊断  接车后试车&#xff0c;起动发动机&#xff0c;可以正常起动着机。使用故障检测仪检测&#x…

Vue项目的创建

安装Vue工具 Vue CLI Vue CLI Vue.js 开发的标准工具&#xff0c;Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统 npm install -g vue/cli安装之后&#xff0c;你就可以在命令行中访问 vue 命令。你可以通过简单运行 vue&#xff0c;看看是否展示出了一份所有可用命令的…

基于SSM邮票鉴赏系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;邮票信息管理&#xff0c;邮票分类管理&#xff0c;文章信息管理&#xff0c;系统管理&#xff0c;个人分享管理 用户账号功能包括&#xff1a;系统首页&#xff0c;个人中心&…

【正点原子K210连载】第四十八章 自学习分类实验 摘自【正点原子】DNK210使用指南-CanMV版指南

第四十八章 自学习分类实验 在上一章节中&#xff0c;介绍了利用maix.KPU模块实现了MNIST的手写数据识别&#xff0c;本章将继续介绍利用maix.KPU模块实现的自学习分类。通过本章的学习&#xff0c;读者将学习到自学习分类应用在CanMV上的实现。 本章分为如下几个小节&#xf…

Hallo2 长视频和高分辨率的音频驱动的肖像图像动画 (数字人技术)

HALLO2: LONG-DURATION AND HIGH-RESOLUTION AUDIO-DRIVEN PORTRAIT IMAGE ANIMATION 论文&#xff1a;https://arxiv.org/abs/2410.07718 代码&#xff1a;https://github.com/fudan-generative-vision/hallo2 模型&#xff1a;https://huggingface.co/fudan-generative-ai/h…

后端C++

前言 1. Task0 1.1 获取你的服务器 1.2 对服务器进行基本操作 分别创建文件夹dir_a, dir_b, dir_c进入dir_a,创建a.txt, b.txt, c.txt 将a.txt, b.txt, c.txt 分别复制成: a.txt.bak, b.txt.bak, c.txt.bak 将a.txt, b.txt, c.txt 分别重命名为: a_new.txt, b_new.txt, c_ne…

凹凸性和拐点的概念

二阶导不存在也可能是拐点 判断拐点的充分条件

Android Studio USB调试真机映射屏幕画面

Android Studio USB调试真机映射屏幕画面 文章目录 Android Studio USB调试真机映射屏幕画面一、USB连手机并设置开发者模式1.1 报错信息1.2 启用开发者选项和 USB 调试&#xff1a;1.3 手机配置选项 二、Android Studio 开启手机投屏功能 一、USB连手机并设置开发者模式 1.1 …

Flutter 小技巧之 equatable 包解析以及宏编程解析

今天我们聊聊 equatable 包的实现&#xff0c;并通过 equatable 去理解 Dart 宏编程的作用和实现&#xff0c;对于 Flutter 开发者来说&#xff0c;Dart 宏编程可以说是「望眼欲穿」。 equatable 正如 equatable 这个包名所示&#xff0c;它的功能很简单&#xff0c;主要是用…