程序化检测内存泄漏

8

如果我故意创建一个应用程序,它在处理数据时会出现内存泄漏,我可以通过以下方式注意到内存使用情况:

Runtime.getRuntime().freeMemory()

开始在1MB到2MB的空闲内存之间震荡。

然后应用程序进入一个循环:GC,处理一些数据,GC等等。但是由于GC发生得如此频繁,应用程序基本上没有做太多其他事情。即使GUI也需要很长时间才能响应(不,我这里没有谈论EDT问题,实际上VM基本上被卡在某种无尽的GC模式中)。

我想知道:是否有一种编程方式来检测JVM没有足够的内存了呢?

请注意,我没有谈论内存不足错误或检测内存泄漏本身。

我所说的是检测一个应用程序运行的内存非常低,以至于它基本上一直在调用GC,几乎没有时间做其他事情(在我的假设例子中:压缩数据)。

例如,重复读取一分钟内可用内存量的方法是否有效,并查看如果数字一直在下面的不同值之间“振荡”,这个数字都在4MB以下,那么就可以得出结论:发生了某些泄漏,应用程序已经无法使用了吗?


2
我认为更好的想法是修复你的代码。 - San Jacinto
3
@San Jacinto:你的评论短视而缺乏意义,对SO没有任何贡献。如果你有任何有价值的东西要在SO上提供,请重新阅读问题和答案。 - SyntaxT3rr0r
3
只有当你的代码出了问题时才会这样。并非所有的第三方软件都是平等的,差距很大。我认为可以插入一个GC钩子……让我看看…… - Mark Storer
1
@Mark Storer:确切地说……此外,对我来说,仅就理论而言,这是一个有趣的问题。但我预计像San Jacinto所做的评论会出现,毕竟这是SO ;) - SyntaxT3rr0r
3
我认为你把短视解释为有吸引力是令人震惊的。 :) - Paul Sonier
显示剩余4条评论
7个回答

3
我在想:是否有一种编程方式可以检测JVM不再具有足够的内存?我认为没有。您可以大致了解在任何给定时刻有多少堆内存空闲,但据我所知,您无法可靠地确定何时会耗尽内存。(当然,您可以执行诸如抓取GC日志文件或尝试选择空闲内存振荡中的模式等操作。但这些可能会因JVM更改而变得不可靠和脆弱。) 但是,还有另一种(并且在我看来更好的)方法。在Hotspot的最新版本(1.6及更高版本)中,您可以调整JVM / GC,以便它更早地放弃并引发OOME。具体而言,可以配置JVM以检查以下内容:在完整GC之后,空闲堆与总堆的比率是否大于给定阈值,以及运行GC所花费的时间是否小于总时间的某个百分比。
相关的JVM参数有"UseGCOverheadLimit"、"GCTimeLimit"和"GCHeapFreeLimit"。不幸的是,Hotspot的调整参数在公共网络上没有很好的文档记录,但这些参数都列在这里
假设您希望应用程序在没有足够内存运行时做出明智的选择...那么只需使用比默认值更小的"GCTimeLimitor"或"GCHeapFreeLimit"启动JVM即可。 编辑 我发现MemoryPoolMXBean API允许您查看单个内存池(堆)的峰值使用情况并设置阈值。然而,我从未尝试过这种方法,并且API中有很多提示表明并非所有JVM都实现了完整的API。因此,我仍然建议使用HotSpot调整选项方法(参见上文)。

2

然而,这并不能为您提供所需的信息以避免GC抖动;即上一次完整GC后立即有多少内存可用/正在使用。为此,您需要使用MemoryPoolMXBean的阈值/通知方法...如果支持的话。 - Stephen C

1

我看到两个攻击向量。

要么监视您的内存消耗。

当您更多或 less 经常使用大量可用内存时,很可能存在内存泄漏(或仅使用了太多内存)。虚拟机将不断尝试释放一些内存,但成功的机会不多 => 常高内存使用率。

您需要将其与经常发生但并非内存问题指标的大zigzag模式区分开来。基本上,您使用越来越多的内存,但是当gc找到时间来执行其工作时,它会发现许多垃圾要清理,因此一切都很好。

另一个攻击向量是监视gc运行的频率和成功的种类。如果它经常运行但只获得少量内存增益,则很可能存在问题。

我不知道您是否可以直接从程序中访问此类信息。但如果没有其他方法,我认为您可以在启动时指定参数,使gc日志信息记录到文件中,然后可以解析该文件。


关于大的锯齿,我在想只有当所有读数都非常接近且都比“X” MB少时才考虑存在问题。 - SyntaxT3rr0r

1
你可以创建一个新的线程,定时唤醒并计算内存使用量并记录结果。然后,你可以对这个结果进行回归分析来估计应用程序内存增长的速率。如果你知道增长速率和最大内存量,就可以预测(有一定的把握)应用程序何时会耗尽内存。

这也是一个非常好的想法。虽然我不确定增长是否是线性的,但我非常喜欢这个想法。 - SyntaxT3rr0r
成长很可能不是线性的 :) - 但我从经验中知道,如果实施得当,结果是有意义的。 - Amir Afghani

0

您可以向Java虚拟机传递参数,以获得GC诊断信息,例如

  1. -verbose:gc此标志打开GC信息记录。在所有JVM中都可用。

  2. -XX:+PrintGCTimeStamps打印GC发生的时间与应用程序启动时间的相对时间。

如果您将该输出捕获到文件中,在应用程序中,您可以定期读取该文件并解析它以了解何时发生了GC。因此,您可以计算出每个GC之间的平均时间。


0

我认为JVM会为您完全处理这个问题,并抛出java.lang.OutOfMemoryError: GC overhead limit exceeded。所以,如果你捕获了OutOfMemoryError并检查该消息,那么你就得到了想要的结果,对吧?

请参阅此问题获取更多详细信息。


哦不不不...在某些情况下,您会遇到我所描述的场景:GC,处理一些数据,GC,处理一些数据。从技术上讲,JVM仍在运行,但实际上它比糖浆还要慢,您可能会杀死应用程序。当然,在很多情况下,您会得到OOM,但在某些情况下,您会得到我所描述的情况。 - SyntaxT3rr0r

0

我一直在使用Plumbr进行内存泄漏检测,这是一次很棒的体验,尽管许可证非常昂贵:http://plumbr.eu/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接