如何监控Java内存使用情况?

时间:2009-06-29 15:41:19

标签: java garbage-collection monitoring memory-management

我们在Jboss上运行了一个j2ee应用程序,我们希望监视它的内存使用情况。目前我们使用以下代码

    System.gc();
    Runtime rt = Runtime.getRuntime();
    long usedMB = (rt.totalMemory() - rt.freeMemory()) / 1024 / 1024;
    logger.information(this, "memory usage" + usedMB);

此代码工作正常。这意味着它显示了与现实相对应的记忆曲线。当我们从DB创建一个大的xml文件时,曲线会上升,在提取完成后,它会下降。

alt text

一位顾问告诉我们,明确调用gc()是错误的,“让jvm决定何时运行gc”。基本上他的论点与disscussed here相同。 但我还是不明白:

  • 我怎样才能获得内存使用曲线?
  • 显式gc()有什么问题?我不关心使用显式gc()可能发生的小的性能问题,我估计会在1-3%。我需要的是内存和线程监视器,它可以帮助我分析我们在客户站点上的系统。

15 个答案:

答案 0 :(得分:50)

如果你想真正了解VM内存中发生的事情,你应该使用像VisualVM这样的好工具。这个软件是免费的,它是查看正在发生的事情的好方法。

使用显式gc()调用,没有什么是“错误的”。但是,请记住,当您调用gc()时,您“建议”垃圾收集器运行。无法保证它会在您运行该命令的确切时间运行。

答案 1 :(得分:28)

有些工具可以让您监控VM的内存使用情况。 VM can expose memory statistics using JMXprint GC statistics。您还可以disable it查看内存随时间的变化情况。

调用System.gc()可能会损害GC的性能,因为对象会过早地从新代移到旧代,弱引用会过早清除。这可能导致内存效率降低,GC时间延长,缓存命中率降低(对于使用弱refs的缓存)。我同意你的顾问:System.gc()很糟糕。我会使用命令行开关到{{3}}。

答案 2 :(得分:10)

您可以查看stagemonitor。它是一个开源的Java(Web)应用程序性能监视器。它捕获响应时间指标,JVM指标,请求详细信息(包括请求探查器捕获的调用堆栈)等。开销非常低。

或者,您可以使用伟大的时间序列数据库石墨来存储数据点的长历史,您可以使用花哨的仪表板查看这些数据点。

实施例: JVM Memory Dashboard

查看project website以查看屏幕截图,功能说明和文档。

注意:我是stagemonitor的开发者

答案 3 :(得分:9)

我会说顾问在理论上是正确的,你在实践中是正确的。正如saying goes

  

理论上,理论和实践是相同的。实际上,他们不是。

Java规范说System.gc建议调用垃圾收集。实际上,它只是生成一个线程并立即在Sun JVM上运行。

虽然从理论上讲你可能会搞乱一些精心调整的垃圾收集JVM实现,除非你编写的是要部署在任何JVM上的通用代码,不要担心它。如果它适合你,那就去做吧。

答案 4 :(得分:8)

答案 5 :(得分:6)

答案 6 :(得分:5)

看看JVM args:http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp#DebuggingOptions

  

XX:-PrintGC在垃圾收集中打印消息。管理。

     

-XX:-PrintGCDetails在垃圾收集中打印更多细节。   管理。 (在1.4.0中引入。)

     

-XX:-PrintGCTimeStamps打印垃圾回收时的时间戳。   可管理(在1.4.0中引入)

     

-XX:-PrintTenuringDistribution打印终身年龄信息。

虽然您不打算通过显式调用System.gc()来破坏JVM,但它们可能没有您期望的效果。要真正了解JVM中的内存是什么,读取Brian Goetz所写的任何内容和所有内容。

答案 7 :(得分:5)

在生产系统上显式运行System.gc()是一个糟糕的主意。如果内存达到任何大小,整个系统可以在完整GC运行时冻结。在一个千兆字节大小的服务器上,这很容易引人注目,这取决于如何配置jvm,它有多少空间等等 - 我已经看到超过30秒的暂停。

另一个问题是,通过显式调用GC你实际上并没有监视JVM如何运行GC,你实际上正在改变它 - 取决于你如何配置JVM,它会在适当的时候进行垃圾收集,并且通常是递增的(它不会在内存不足时运行完整的GC)。你将要打印出来的内容与JVM独自完成的内容完全不同 - 一方面,你可能会看到更少的自动/增量GC,因为你将手动清除内存。

正如尼克霍尔特的帖子指出的那样,打印GC活动的选项已作为JVM标志存在。

你可以让一个线程只是以合理的间隔自由打印并且可用,这将显示实际的mem使用情况。

答案 8 :(得分:4)

如果您喜欢从命令行执行此操作,请使用jstat:

http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jstat.html

它以可配置的间隔提供原始信息,这对于记录和绘图非常有用。

答案 9 :(得分:2)

如果您使用java 1.5,您可以查看ManagementFactory.getMemoryMXBean(),它可以为您提供 各种记忆中的数字。堆和非堆,perm-gen。

那里可以找到一个很好的例子 http://www.freshblurbs.com/explaining-java-lang-outofmemoryerror-permgen-space

答案 10 :(得分:2)

如果您使用JMX提供的GC运行历史记录,您可以使用相同的前/后数字,您只需要强制GC。

您只需要记住那些GC运行(通常一个用于旧运行,一个用于新一代)不在常规的intervalls上,因此您需要提取开始时间以及绘图(或者您根据序列号绘图) ,对于大多数实际目的而言,足以进行绘图)。

例如在带有ParNewGC的Oracle HotSpot VM上,有一个名为java.lang:type=GarbageCollector,name=PS Scavenge的JMX MBean,它有一个属性LastGCInfo,它返回上一个YG清道夫运行的CompositeData。它记录为duration,绝对startTimememoryUsageBefore以及memoryUsageAfter

只需使用计时器即可读取该属性。每当新的startTime出现时,您知道它描述了一个新的GC事件,您将提取内存信息并继续轮询以进行下一次更新。 (不确定是否可以以某种方式使用AttributeChangeNotification。)

提示:在您的计时器中,您可能会测量到最后一次GC运行的距离,如果这对于您的绘图结果太长,您可以有条件地调用System.gc()。但我不会在OLTP实例中这样做。

答案 11 :(得分:0)

按照建议,尝试使用VisualVM获取基本视图。

您还可以使用Eclipse MAT进行更详细的内存分析。

只要你不依赖它就可以使用System.gc(),以确保程序的正确性。

答案 12 :(得分:0)

system.gc的问题在于JVM已根据内存使用情况自动为垃圾收集器分配时间。

但是,例如,如果你是在一个非常受内存限制的情况下工作,比如移动设备,System.gc允许你手动为这个垃圾收集分配更多的时间,但代价是cpu时间(但是,如你所说,你并不关心gc的性能问题。

最佳做法可能仅在您可能正在进行大量释放的情况下使用它(如刷新大型数组)。

所有人都考虑过,因为你只关心内存使用情况,随时可以调用gc,或者更好的是,看看它是否会在你的情况下产生很大的内存差异,然后再决定。

答案 13 :(得分:0)

关于System.gc()...我只是在Oracle文档中阅读了以下句子here

可以通过使用标志-XX:+ DisableExplicitGC禁用显式垃圾回收来评估显式垃圾回收的性能,这将导致VM忽略对System.gc()的调用。

如果您的VM供应商和版本支持该标志,则可以在有和没有该标志的情况下运行代码,并比较性能。

还请注意,在前面引用的句子之前有这句话:

这可能会强制在没有必要的情况下(例如,当次要收集就足够时)进行主要收集,因此通常应避免使用。

答案 14 :(得分:0)

JavaMelody可能是您所需的解决方案。

该工具是为Java EE应用程序开发的,可测量并生成有关您的应用程序在任何环境下的实际操作的报告。它是免费和开源的,易于集成到具有一定历史的应用程序中,无需数据库或配置文件,非常轻巧。