【译】Java 内存泄露的构造和检测

news2024/9/22 15:49:34

1. 概述

在 Java 应用程序中,内存泄漏会导致严重的性能下降和系统故障。开发人员必须了解内存泄漏的发生原因以及如何识别和解决它们。

在本教程中,我们将提供一个使用失效的监听器问题作为示例来创建 Java 内存泄漏的指南。我们还将讨论各种检测内存泄漏的方法,包括日志记录、分析、详细垃圾回收和堆转储。

2. 构造内存泄漏

我们将考虑失效的监听器问题作为内存泄漏的示例。这是学习Java中内存分配和垃圾回收的一个很好的方式。

让我们创建一个应用程序,向已登录并订阅我们的服务的用户发送随机电影名言。这个应用程序非常简单,一次只能为一个用户提供服务:

public static void main(String[] args) {
    while (true) {
        User user = generateUser();
        logger.debug("{} logged in", user.getName());
        user.subscribe(movieQuoteService);
        userUsingService();
        logger.debug("{} logged out", user.getName());
    }
}

_UserGenerator _是一个简单的类,提供无限的随机用户。我们将使用 Datafaker 进行随机化:

public class UserGenerator {

    private final static Faker faker = new Faker();

    public static User generateUser() {
        System.out.println("Generating user");
        String name = faker.name().fullName();
        String email = faker.internet().emailAddress();
        String phone = faker.phoneNumber().cellPhone();
        String street = faker.address().streetAddress();
        String city = faker.address().city();
        String state = faker.address().state();
        String zipCode = faker.address().zipCode();
        return new User(name, email, phone, street, city, state, zipCode);
    }
}

用户与我们的服务之间的关系将基于观察者模式。因此,_Users _可以订阅服务,我们的 MovieQuoteService 将向用户更新新的电影名言。

此示例的主要问题是,_Users _从未从服务中取消订阅。 这会导致内存泄漏,即使用户超出了范围,也不能通过垃圾收集器删除它们,因为服务保留了它们的引用。

我们可以明确取消订阅用户来减轻此问题,这将起作用。但是,最好的解决方案是使用 WeakReferences 来自动化此过程。

3. 检测内存泄漏

在上一节中,我们创建了一个存在严重问题的应用程序——内存泄漏。尽管这个问题可能是灾难性的,但通常很难检测到。

3.1. 日志记录

让我们从最简单的方法开始,使用日志记录来查找系统中的问题。这不是检测内存泄漏的最高级方法,但它易于使用,可能有助于发现异常。

在运行我们的服务时,日志输出会显示用户活动:

21:58:24.280 [pool-1-thread-1] DEBUG c.b.lapsedlistener.MovieQuoteService - New quote: Go ahead, make my day.
21:58:24.358 [main] DEBUG c.b.l.LapsedListenerRunner - Earl Runolfsdottir logged in
21:58:24.358 [main] DEBUG c.b.lapsedlistener.MovieQuoteService - Current number of subscribed users: 0
21:58:24.371 [main] DEBUG c.b.l.LapsedListenerRunner - Earl Runolfsdottir logged out
21:58:24.372 [main] DEBUG c.b.l.LapsedListenerRunner - Barbra Rosenbaum logged in
21:58:24.372 [main] DEBUG c.b.lapsedlistener.MovieQuoteService - Current number of subscribed users: 1
21:58:24.383 [main] DEBUG c.b.l.LapsedListenerRunner - Barbra Rosenbaum logged out
21:58:24.383 [main] DEBUG c.b.l.LapsedListenerRunner - Leighann McCullough logged in
21:58:24.383 [main] DEBUG c.b.lapsedlistener.MovieQuoteService - Current number of subscribed users: 2
21:58:24.396 [main] DEBUG c.b.l.LapsedListenerRunner - Leighann McCullough logged out
21:58:24.397 [main] DEBUG c.b.l.LapsedListenerRunner - Mr. Charlie Keeling logged in
21:58:24.397 [main] DEBUG c.b.lapsedlistener.MovieQuoteService - Current number of subscribed users: 3
21:58:24.409 [main] DEBUG c.b.l.LapsedListenerRunner - Mr. Charlie Keeling logged out
21:58:24.410 [main] DEBUG c.b.l.LapsedListenerRunner - Alvin O'Connell logged in
21:58:24.410 [main] DEBUG c.b.lapsedlistener.MovieQuoteService - Current number of subscribed users: 4
21:58:24.423 [main] DEBUG c.b.l.LapsedListenerRunner - Alvin O'Connell logged out
21:58:24.423 [main] DEBUG c.b.l.LapsedListenerRunner - Tracey Stoltenberg logged in
21:58:24.423 [main] DEBUG c.b.lapsedlistener.MovieQuoteService - Current number of subscribed users: 5

我们可以在上面的片段中注意到一个有趣的事情。如前所述,我们的应用程序一次只能处理一个用户。

因此,只有一个用户可以订阅我们的服务。与此同时,日志显示订阅者的数量超过了这个值。 进一步阅读提供了更多证据,证明我们的系统存在问题。

尽管日志没有显示问题发生的地方,但这是防止系统出现问题的第一步。

3.2. 性能分析

与前一个步骤一样,此步骤旨在找到正在运行的应用程序中的异常。然而,性能分析器 可以显著简化对正在运行的应用程序的内存占用的监控。

首先要注意的是,随着时间的推移,使用的内存单调增加。 这并不总是内存泄漏的标志。然而,在像我们这样的应用程序上,内存使用的增加可能是我们有问题的一个很好的迹象。

我们将使用 JConsole 分析器。这是一个基本的分析器,但它提供了所有所需的功能,并包含在每个 JDK 分发中。另外,它很容易在任何系统上启动:

$ jconsole

让我们启动应用程序,看看 JConsole 会告诉我们什么。在启动应用程序后,其内存消耗增加:

在这里插入图片描述

然而,内存使用并不总是内存泄漏的迹象。

让我们尝试提示垃圾收集器清理一些死亡对象:

在这里插入图片描述

如我们所见,垃圾收集器工作得相当好,清理了一些空间。因此,我们可以假设我们根本没有任何问题。然而,让我们看看 Old Generation。这是应用程序中一些对象经过几次垃圾回收后依然存在的空间。我们可以看到它的大小不断增加:

在这里插入图片描述

一个解释是,除了用户之外,我们还有引用。我们的应用程序中没有存储引用的引用,所以垃圾收集器在清理它们时没有问题。与此同时,我们的服务保留了对每个用户的引用,阻止它们被垃圾收集,并将它们提升到 Old Generation:
在这里插入图片描述

尽管垃圾收集器定期清理,但很明显,总体内存消耗随着时间的推移在增长。我们在几分钟内从大约 10 MB 增加到了 30 MB。 在服务器上,这可能几个小时甚至几天都不会造成任何问题。如果服务器定期重启,我们可能永远不会看到 OutOfMemoryError

在这里插入图片描述

我们在 old generation 中也有同样的情况:内存消耗只是在增长。对于我们这样的应用程序,一次只能为一个用户提供服务,这是一个问题的迹象。

3.3. 查看详细垃圾回收日志

这是另一种检查堆状态和垃圾回收过程的方法。根据 Java 版本,我们可以使用一些标志来开启详细垃圾回收。日志输出将反映我们在 JConsole 中获得的先前信息:

[0.004s][info][gc] Using G1
[0.210s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 23M->6M(392M) 1.693ms
[33.169s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 38M->7M(392M) 1.994ms
[250.890s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 203M->16M(392M) 11.420ms
[507.259s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 228M->25M(392M) 14.321ms
[786.181s][info][gc] GC(4) Pause Young (Normal) (G1 Evacuation Pause) 229M->33M(392M) 17.410ms
[1073.277s][info][gc] GC(5) Pause Young (Normal) (G1 Evacuation Pause) 241M->41M(392M) 11.251ms
[1341.717s][info][gc] GC(6) Pause Young (Normal) (G1 Evacuation Pause) 241M->48M(392M) 17.132ms

这些日志使用特定格式来显示随着时间的推移,整体内存消耗增加。这是检查应用程序的内存占用并查找问题的快速简便方法。

然而,在这一步之后,我们需要找到这个问题的原因。在我们只有几个类的应用程序中,任务可能是微不足道的,我们可以通过审查我们的代码来解决它。与此同时,在一个庞大的应用程序中,仅通过查看代码可能无法检测到问题。

3.4. 堆转储

有几种方法可以捕获堆转储,JDK 包括几个控制台工具。我们将使用 VisualVM 来捕获和阅读堆转储:
在这里插入图片描述

这是一个方便的工具,可以捕获堆转储,并包含 JConsole_的所有功能,使过程变得非常简单。

在捕获堆转储后,我们可以回顾并分析它。在我们的例子中,我们将尝试找到不应该存在的活动对象。幸运的是,VisualVM 为堆转储生成了一个概要,显示了重要的信息:

在这里插入图片描述

在我们的系统中,用户在实例数量和整体大小方面排名第三。我们已经知道我们有一个内存消耗问题,现在我们找到了罪魁祸首。

此外,VisualVM 还允许我们更详细地分析堆转储,并检查堆中的所有实例:

在这里插入图片描述

这在具有复杂对象交互的大型应用程序中可能非常有帮助。此外,这对于调整应用程序和找到问题区域可能也很有用。

在找到问题实例后,我们仍然需要检查代码以查看内存泄漏何时出现,但现在我们可以缩小搜索范围。

4. 结论

内存泄漏会对 Java 应用程序产生重大影响,导致内存逐渐耗尽和潜在的系统故障。在本教程中,我们为教学目的创建了一个内存泄漏,并讨论了各种检测技术,包括日志记录、分析、查看详细垃圾回收和堆转储。

每种方法都可以提供有关应用程序运行时行为和内存消耗的有价值的见解。日志记录有助于识别异常,而分析和详细垃圾回收日志监视内存使用情况和垃圾回收过程。堆转储可以识别出问题对象及其引用,缩小内存泄漏的来源。

了解 Java 中的内存分配和垃圾回收有助于开发人员防止内存泄漏并构建更高效、健壮的应用程序。 与往常一样,源代码可以在 GitHub 上找到。

原文地址:https://www.baeldung.com/java-create-detect-memory-leaks

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

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

相关文章

GitHub Copilot 使用介绍

什么是 Github Copilot https://github.com/features/copilot Github Copilot 是一种人工智能辅助开发工具,由 GitHub 和 OpenAI 合作开发,旨在通过机器学习算法为开发人员提供自动化的代码提示和智能建议,从而提高开发效率,该系…

【Vue】 CLI WebStorage

CLI 本地存储 自定义事件 WebStorage(js本地存储)localStoragesessionStorage WebStorage(js本地存储) 存储内容大小一般支持5MB左右(不同浏览器可能还不一样) 浏览器端通过Window.sessionStorage 和Winodw.localStorage 属性来实现本地存储机制相关API xxxStorage.setItem(‘…

TCP协议介绍

文章目录 一、TCP协议二、TCP协议段格式4位首部长度可靠性理解32位序号和32位确认序号16位窗口大小TCP协议中的6个标记位16位紧急指针 三、TCP三次握手和四次挥手TCP的三次握手TCP的四次挥手状态变化 四、超时重传机制五、滑动窗口高速重发机制(快重传) 六、流量控制七、拥塞控…

Java分布式事务(十八)

文章目录 🔥最终一致性分布式事务解决方案_什么是最大努力通知型分布式事务🔥最大努力通知型分布式事务_最大努力通知与可靠消息最终一致性的区别🔥最大努力通知型分布式事务解决方案🔥最大努力通知型分布式事务_案例业务说明🔥最大努力通知型分布式事务实战_实现充值…

Adobe XD 下载和安装教程

文章目录 Adobe XD 简介Adobe XD 下载Creative Cloud 下载与安装Adobe XD 下载 Adobe XD 安装Adobe XD 启动 Adobe XD 简介 Adobe XD是一个协作式易用平台,可帮助团队为网站、移动应用程序、语音界面、游戏等创建设计。 Adobe XD是一站式UX/UI设计平台,…

能否实现有价值观的--AI ?

人机融合所形成的新系统确实有可能产生新的科技革命。人机融合可以将人类的智慧和创造力与机器的计算能力和数据处理能力相结合,从而创造出更加智能化和高效化的新系统和产品。例如,人机融合可以推动智能制造、智能医疗、智能交通、智慧城市等各个领域的…

项目问题~

一、 国际化(i18n)北京外国语大学27种语言 vue-i18n i18n Ally xlsx vue-json-excel 1. 开始开发用zh-cn.json的文件写完页面逻辑 2. 通过公司免费的api接口将每个字段翻译成27种语言(生成一个大对象) 3. …

粒子群算法(PSO)

理论: 粒子群优化算法(PSO)是一种智能优化算法,也是一种元启发式算法,最初是由Eberhart和Kennedy提出的,其模拟了鸟群捕食行为,通过一定的搜索策略,使得多个粒子在多维搜索空间中寻…

【Linux】Linux下安装XXL-JOB(图文解说详细版)

文章目录 前言安装xxl-jobstep1:上传step2:解压step3:初始化数据库step4:修改xxl-job配置文件step4.1:修改调度中心配置文件application.propertiesstep4.2:修改logback.xml(配置xxl-job的日志路径)step4.3…

树莓派3b+ 修改USB串口的波特率

树莓派3b 修改USB串口波特率 1. 背景:需要多个RS232串口2.问题:多个串口波特率不同3.解决方案4.完美方案错误方案集锦使用minicom设置ttyUSB0的波特率并保存 1. 背景:需要多个RS232串口 树莓派3B只有两个串口,一个硬件串口&#…

CSAPP Lab3- bufbomb

实验目标 (1)掌握函数调用时的栈帧结构 (2)利用输入缓冲区的溢出漏洞,将攻击代码嵌入当前程序的栈帧中,使得程序执行我们所期望的过程 实验代码 (1)makecookie:生成c…

解读IEC有关“雷击防护”的新要求

背景介绍 TC64标委会在IEC 组织里负责低压电气装置安全规范的起草和编制,主要出版物有:IEC60364、IEC61140、IEC60479等。 在国内由TC205标委会负责参与TC64活动、等同转化的出版物,如:IEC60364等同转化为GB16895系列规范等。 …

2023零售店铺管理系统最新排名,这5款性价比高!

很多零售店铺的老板,每天都在被开单收银、记账对账、商品销售、销售数据等各种琐事困扰,使用传统的人工管理模式,耗费了大量的时间和成本,也没有达到理想的效果。 其实,零售店铺管理也可以很简单省事,借助零…

[Java基础练习-002]综合应用(基础进阶)

1、进阶练习一、学习网站(主要考察选择结构、输入语句) 请使用现有的java基础知识开发一个学习网站主要实现如下功能: (1)、显示第一层登录菜单,选择登录后再显示第二层主菜单,否则退出系统&a…

虹科分享|终端安全防护|您的遗留系统的安全性如何?

自2023年1月10日起,Windows 7、Windows 8、Windows 8.1及其衍生产品Windows Embedded以及Windows Server 2008 R2将不再收到微软提供的补丁程序。数以百万计的设备现在将成为“遗留”设备,并产生一系列新的遗留安全风险。 Windows 7支持结束&#xff0c…

哪种蓝牙耳机戴着耳朵不痛?佩戴舒适不挑耳朵的蓝牙耳机

蓝牙耳机早已成为了人们使用手机时的下意识选择,目前蓝牙耳机品牌也多、种类也多,不知道你会不会有选择困难症?综合大家关注的点,小编发现佩戴舒适度这方面是大家较为注意的,下面小编整理了一期佩戴舒适不挑耳的蓝牙耳…

递归算法求数组中的最值

一、关于递归算法和一般算法 递归算法和一般算法相比,递归算法的思维量会大很多,不仅很难理解,用起来还容易出错,所以大家平时用一般算法就足够了。就拿今天我们要实现的“求数组中的最值”来举例吧,用一般算法一个循…

用SD完美还原金庸小说名场面;产品经理用ChatGPT提效520倍;盘点13个SD最强模型;中国移动推出周杰伦数智人 | ShowMeAI日报

👀日报&周刊合集 | 🎡生产力工具与行业应用大全 | 🧡 点赞关注评论拜托啦! 🤖 『ChatGPT访问量再创新高』强势挑战谷歌、百度的霸主地位 SimilarWeb 的统计数据显示,ChatGPT 在4月份的全球访问量已经达…

Tektronix泰克TCP2020 AC/DC电流探头

特征: 20 ARMS 最大电流能力 100 A 峰值脉冲电流能力 10 毫安/毫伏灵敏度 BNC 输出连接到大多数示波器和其他测量 设备(需要 >100 kΩ 端接) 用于直流偏移调整的消磁按钮和指轮 由交流适配器供电 易于使用且准确的交流/直流电流测量 DC 至…

专业恢复电脑数据软件Easyrecovery16

Easyrecovery是一款强大的数据恢复软件,它专门解决磁盘数据恢复问题。在计算机世界里,数据丢失经常是一件令人头疼的事情,但是有了Easyrecovery,您可以放心大胆地享受数据备份和恢复的乐趣。 Easyrecovery具有快速和有效的数据恢复…