Java代码中的内存泄漏

-jar java_app.jar

这基本上启动探查器,禁用所有安全性并告诉它转储到一个名为hprof.bin文件. 如果您的程序不在jar文件中,您可以简单地用应用程序的主类替换最后一行。现在练习您的程序,完成后,按Ctrl-C转储堆并退出应用程序。

使用Jmap

======

正常启动程序,可以从命令行、文件管理器(Finder或Windows资源管理器或现在Lunix用户使用的任何讨厌的工具)或IDE启动程序。然后找到程序的进程id。您可以用几种不同的方法来实现这一点,但更简单的方法是查看JConsole,在JConsole中可以使用它。在上面的示例中,进程id是59735(请参阅进程选择屏幕上的PID列或监视进程时的标题栏)。现在练习你的程序。

然后使用jmap转储图像:

jmap -dump:format=b,file=hprof.bin 59735

记住用JConsole找到的进程id替换59735。

训练你的程序

======

我们现在从两个不同的分支合并。你的读者没有比你更愉快的阅读体验,但是你呢?嗯,我们需要锻炼我们的计划。也就是说,执行我们认为会导致错误的操作。在我的例子中,我应该启动程序并让它运行,但是你可能需要加载一个文件,打印它或者你的程序做什么。重复几次,这样堆中的错误就更明显了。

分析堆映像

=====

当我们使用任何一种方法生成了一个堆映像时,就应该分析它了。幸运的是,JDK有这样一个工具,jhat(我不知道是谁想出了这些名字,但他们应该被枪杀)。以以下方式开始jhat:

jhat -J-Xmx2G hprof.bin

注意-J和-Xmx2G之间没有空格,我们设置这个选项是为了允许jhat使用2GB的RAM,这可能需要它来分析我们相当大的堆映像。

jhat将处理(这可能需要很长时间),并在本地计算机上的端口7000上启动一个web服务器,因为为什么不呢。要查看生成的数据,请将浏览器指向localhost:7000。你会得到这样的结果:

立即滚动到底部:

选择Show heap histogram链接(注意:你的浏览器可能没有一个红色的矩形指出正确的链接,正如我在后处理中添加的那样)。对于我的应用程序,如果使用hprof,柱状图如下所示:

或者像这样如果我用jhat的话:

数字不同,因为hprof的时间取决于我愿意等待多长时间,而jmap的时间取决于我转储图像的速度。注意,jmap输出并不总是显示好的类名。在下面,我将只显示hprof输出,因为它更好,但是使用jmap输出也可以完成任何事情。

这个输出应该像一个普通的profiler输出一样阅读:关注那些你拥有很多的输出。在我的例子中,我有一大堆公式和HashMap E n t r y 类。事实上,我比其他任何东西都多了几百个。这是有道理的,我有一堆公式类,因为我处理公式,但不超过一百万。虽然数据结构是递归的,但对于一个复杂的公式,我可能会用几千,而不是一百万。另外, H a s h M a p Entry类。事实上,我比其他任何东西都多了几百个。这是有道理的,我有一堆公式类,因为我处理公式,但不超过一百万。虽然数据结构是递归的,但对于一个复杂的公式,我可能会用几千,而不是一百万。另外,HashMap Entry类。事实上,我比其他任何东西都多了几百个。这是有道理的,我有一堆公式类,因为我处理公式,但不超过一百万。虽然数据结构是递归的,但对于一个复杂的公式,我可能会用几千,而不是一百万。另外,HashMap条目也吸引了我。这表示某个东西存储在HashMap中,并且比较这些数字,表明某些东西可能只是公式。查看Formula类没有显示HashMap,所以我有点不知所措:什么包含所有这些公式?

为了调查,让我们看看所有这些公式。我们点击公式得到这个屏幕:

快速地按类型链接引用摘要,因为它开始加载一个很长的列表,其中包含超过一百万个引用,这可能会杀死你的浏览器。然后我们看到:

这告诉我们两件事:Formula显然是递归数据类型(大多数实例由其他Formula对象引用),其余的由HashMap E n t r y 对象引用。第三个地方的公式数组是可以的,因为它包含生成公式的模板(我可以通过链接看到这一点,但我没有在这里显示)。让我们轻点 H a s h M a p Entry对象引用。第三个地方的公式数组是可以的,因为它包含生成公式的模板(我可以通过链接看到这一点,但我没有在这里显示)。让我们轻点HashMap Entry对象引用。第三个地方的公式数组是可以的,因为它包含生成公式的模板(我可以通过链接看到这一点,但我没有在这里显示)。让我们轻点HashMap条目,看看它会带我们去哪里。它把我们带到这里:

因此,它们要么从数组引用,要么从其他HashMap E n t r y 对象引用。 G u e s s J a v a 的哈希表是通过使用 H a s h M a p Entry对象引用。Guess Java的哈希表是通过使用HashMap Entry对象引用。GuessJava的哈希表是通过使用HashMapEntry对象的链表来处理冲突的条目数组来实现的。18000次碰撞并不是很好…总之,让我们看看数组:

毫不奇怪,这些都在HashMaps中。以下内容:

这些代码在很多地方都有使用,但是我的代码只在少数地方出现。节点非常有意义:我确实有一些引用节点的图,节点的数量似乎很小,可以理解。SinglePropertyGraph还包含公式,而且对象的数量(1)再次非常合理。不过,DefaultFormulaFactory有点奇怪。为什么它要有一个公式图?而且,它是唯一一个在我的代码执行过程中存在的对象。启动Eclipse并加载工厂:

好像我们找到了罪魁祸首。工厂规范化公式对象。这显然是不好的,当你产生数以百万计。规范化对象的原因是为了允许快速比较,所以我可以使用WeakReferences来解决这个问题,但是我使Formula对象不可变(无法修改),并添加了哈希值的预计算和有效的相等函数:

现在,我刚刚摆脱了规范化,取而代之的是更有效的哈希函数和等式测试。然后我运行我的回归测试,以确保我没有破坏任何东西(回归测试创建起来很糟糕,但当你做这样的事情时,它们真的很震撼)。

验证内存泄漏是否消失

==========

为了验证这是否真的修复了内存泄漏,我再次尝试JConsole,它现在显示了如下内容:

即使随着时间的推移,我们的内存消耗也不再稳定增长。钉子没问题。

为了确保我没有错过任何东西,我再开始我的程序。我让运行几秒钟并创建一个转储(使用jmap),让程序继续运行一两分钟并创建一个辅助转储(到一个新文件)。

然后我们又去查jhat,这次

jhat -J-Xmx2G -baseline heap1.bin heap2.bin

这里heap1.bin是我修复错误后生成的第一个堆映像,heap2.bin是我让程序运行几分钟后创建的映像。-baseline选项告诉jhat使用第一个堆映像作为基线,并将两个映像中的任何对象标记为“old”,将仅在后一个映像中的任何对象标记为“new”。启动浏览器,我们现在选择显示实例计数链接整洁的底部:

这显示了有多少实例以及其中有多少是新实例:

这表明,虽然我仍然有很多公式实例,但所有这些都是新的,因此我没有泄漏任何公式实例。任何与HashMap相关的问题也是如此。事实上,我看到几乎所有的对象都是新的,这是不寻常的,但对我的程序来说是非常有意义的,它分配一堆公式,测试它们,转储它们,然后丢弃它们。

结论

==

在这里,我们看到了如何使用JDK提供的工具来消除程序中的内存泄漏。我的程序有点特别,因为随着时间的推移,它几乎会转储内存中的所有内容,使得检测内存泄漏特别容易。不过,用于验证内存泄漏是否已被发现和删除的技术也可以用于一般情况:例如,启动程序并使其处于稳定状态。然后倒垃圾。现在练习你的程序,使它回到稳定状态。然后创建一个新的转储,并将其与旧转储进行比较。您应该几乎看不到任何新对象-新对象对应于泄漏

您还可以探索程序的一些选项;例如,jmap有一个-histo选项,它可以立即在控制台中显示内存历史记录,这比生成和分析转储文件要快得多。

除此之外,它只是关于在时间和空间上对代码进行分析,并确保在出于性能原因对工作程序进行更改之前拥有可靠的回归测试套件。

原文链接:http://javakk.com/1140.html
如果觉得本文对你有帮助,可以关注一下我公众号,回复关键字【面试】即可得到一份Java核心知识点整理与一份面试大礼包!另有更多技术干货文章以及相关资料共享,大家一起学习进步!

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!**

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值