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

问题描述 投票:56回答:13

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

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

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

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

  • 我怎么能有我的记忆力使用曲线?
  • 显式gc()有什么问题?我不关心使用显式gc()可能发生的小的性能问题,我估计会在1-3%。我需要的是内存和线程监视器,它可以帮助我分析我们在客户站点上的系统。
java garbage-collection monitoring memory-management
13个回答
51
投票

如果你想真正了解VM内存中发生的事情,你应该使用像VisualVM这样的好工具。这个软件是免费的,这是一个很好的方式来看看发生了什么。

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


2
投票

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

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


2
投票

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

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

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

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

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


0
投票

正如所建议的那样,尝试使用VisualVM获取基本视图。

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

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


0
投票

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

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

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

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


28
投票

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

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


10
投票

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

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

例:

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

注意:我是stagemonitor的开发者


9
投票

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

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

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

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



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写的所有东西。


5
投票

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

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

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

你可以有一个线程,只需要以合理的间隔打印出免费和可用的线程,这将显示实际的mem使用情况。


4
投票

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.