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

这篇具有很好参考价值的文章主要介绍了【译】Java 内存泄露的构造和检测。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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 会告诉我们什么。在启动应用程序后,其内存消耗增加:

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

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

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

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

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

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

一个解释是,除了用户之外,我们还有引用。我们的应用程序中没有存储引用的引用,所以垃圾收集器在清理它们时没有问题。与此同时,我们的服务保留了对每个用户的引用,阻止它们被垃圾收集,并将它们提升到 Old Generation:
【译】Java 内存泄露的构造和检测

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

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

我们在 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 来捕获和阅读堆转储:
【译】Java 内存泄露的构造和检测

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

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

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

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

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

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

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

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

4. 结论

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

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

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

原文地址:https://www.baeldung.com/java-create-detect-memory-leaks文章来源地址https://www.toymoban.com/news/detail-438502.html

到了这里,关于【译】Java 内存泄露的构造和检测的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 构建 ESLint 内存泄露检测插件入门:提升代码质量与防范运行时风险

    前言 本文目的是介绍如何创建开发一个自定义规则 ESLint 插件。利用其能力,检测一些代码中可能存在的内存泄露并及时进行提示,避免潜在的后期影响。 本文实现其中一部分功能–检测事件监听器的使用是否存在内存泄露为例来演示基本的 ESLint 自定义规则插件开发的过程

    2024年04月27日
    浏览(65)
  • Java 内存泄露问题详解

    目录 1、什么是内存泄露? 2、Java 中可能导致内存泄露的场景 3、长生命周期对象持有短生命周期对象引用造成的内存泄露问题示例 4、静态集合类持有对象引用造成内存泄露问题的示例 1、什么是内存泄露?         内存泄露指的是程序运行时未能正确释放不再使用的内

    2024年02月09日
    浏览(41)
  • Java中的内存泄露、内存溢出与栈溢出

    大家好,我是欧阳方超。本次就Java中几个相似而又不同的概念做一下介绍。内存泄漏、内存溢出和栈溢出都是与内存相关的问题,但它们之间有所不同。 我们经常会遇到内存泄漏、内存溢出和栈溢出等问题,这些问题都与内存的使用有关。 内存泄漏(memory leak)指的是程序

    2024年02月03日
    浏览(72)
  • Java 使用 VisualVM 排查内存泄露

    线上突发告警,笔者负责的一个服务 老年代内存使用率到达 75% 阈值 ,于是立即登录监控系统查看数据。拉长时间周期,查看最近 7 天的 GC 和老年代内存占用,监控截图如下。 可以看到老年代占用内存的最低点在逐步抬升,初步判断是发生了内存泄露 2.1 初步排查 从监控上

    2024年02月15日
    浏览(53)
  • 【物联网】物联网设备和应用程序涉及协议的概述

    物联网设备和应用程序涉及协议的概述。帮助澄清IoT层技术栈和头对头比较。 物联网涵盖了广泛的行业和用例,从单一受限制的设备扩展到大量跨平台部署嵌入式技术和实时连接的云系统。 将它们捆绑在一起是许多传统和新兴的通信协议,允许设备和服务器以新的,更互联

    2024年01月18日
    浏览(48)
  • 【JVM】Java内存泄露的排查思路?

    Java内存泄露(Memory Leak)是指在Java程序中,无用的对象占用了 堆内存 ,但无法被垃圾回收器回收释放,从而导致可用内存逐渐减少,最终可能导致内存耗尽或性能下降的问题。 说明一般对于内存泄漏。都是针对 堆 的。 程序一般出现内存泄漏会有 两个状态 一是一启动导致

    2024年02月13日
    浏览(50)
  • Linux内存从0到1学习笔记(6.12 应用程序是如何申请内存的呢?)

        前面提到了应用程序大多基于glibc的malloc/free进行内存的分配。这里不讨论共享内存,因为共享内存都是预先分配好的,所以由共享内存mmap和shm所设计的内存泄漏比较少见。     接下来我们从应用调用的维度来看下,应用程序都有哪些调用入口,以及它们是如何申请和释

    2024年02月03日
    浏览(64)
  • 注意避坑!Java 内部类持有外部类会导致内存泄露。。。

    本文介绍 Java 内部类持有外部类导致内存泄露的原因以及其解决方案。 为什么内部类持有外部类会导致内存泄露 非静态内部类会持有外部类,如果有地方引用了这个非静态内部类,会导致外部类也被引用,垃圾回收时无法回收这个外部类(即使外部类已经没有其他地方在使

    2024年02月09日
    浏览(51)
  • win--C盘程序员常见应用内存空间处理

    写在前面: 本篇用于记录我对于C盘各个应用内存处理的总结, 在win中有着这样一个命令mklink,可以在win系统中构建链接,就可以完成一些将C盘内容移动到D盘,但不需要修改软件配置文件的操作。 命令格式 例 复制.vscode文件 在C盘C:Users${用户名}中复制.vscode到需要的目录假

    2024年02月05日
    浏览(51)
  • 【ARMv7-M】| 01——阅读笔记 | 简介|应用程序级编程和内存模型

    系列文章目录 【ARMv7-M】| 01——阅读笔记 | 简介|应用程序级编程和内存模型 失败了也挺可爱,成功了就超帅。 本文为ARMv7-M参考手册的阅读笔记 ARMv7架构根据不同应用场景和性能 分为三个分支 -A -R -M ARMv7-A:用于高性能应用型SOC 支持ARM和Thumb指令集 内存管理单元使用 MMU 支持

    2024年04月27日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包