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

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

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

这段代码工作正常。 这意味着它显示出符合现实的记忆曲线。 当我们从一个数据库创build一个大的xml文件时,曲线就会上升,提取完成后就会停止。

替代文字

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

  • 我怎么能有我的记忆使用曲线?
  • 显式的gc()有什么问题? 我不在意使用明确的gc()可能发生的小的性能问题,我估计这个问题在1-3%之内。 我需要的是内存和线程监视器,它可以帮助我分析我们的系统在客户现场。

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

明确的gc()调用没有什么是“错误的”。 但是,请记住,当您调用gc()时,您正在“build议”垃圾回收器正在运行。 不能保证它会在你运行这个命令的时候运行。

有一些工具可以让你监视虚拟机的内存使用情况。 VM可以使用JMX公开内存统计信息 。 您也可以打印GC统计信息来查看内存在一段时间内的运行情况。

调用System.gc()可能会损害GC的性能,因为对象将被过早地从新一代移到旧一代,并且弱引用将被过早地清除。 这会导致内存效率降低,GC时间延长,caching命中率下降(对于使用弱引用的caching)。 我同意你的顾问:System.gc()是不好的。 我会尽可能使用命令行开关来禁用它 。

我会说顾问在理论上是正确的,而且你在实践中是正确的。 俗话说 :

理论上讲,理论和实践是一样的。 实际上,他们不是。

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

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

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

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

例: JVM内存仪表板

查看项目网站 ,查看截图,function描述和文档。

注意:我是Stagemonitor的开发者

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

XX:-PrintGC在垃圾回收处打印消息。 pipe理。

-XX:-PrintGCDetails在垃圾收集处打印更多细节。 pipe理。 (在1.4.0中介绍)

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

-XX:-PrintTenuringDistribution打印持久性年龄信息。

虽然你不打算通过显式调用System.gc()来打乱JVM,但是它们可能没有你期望的效果。 要真正理解一下JVM中的内存是如何读取的, Brian Goetz写的所有内容。

通过Visual VM查看tomcat内部正在发生的事情。 http://www.skill-guru.com/blog/2010/10/05/increasing-permgen-size-in-your-server/ 替代文字

替代文字

在生产系统上显式运行System.gc()是一个糟糕的主意。 如果内存达到任何大小,整个系统可能会在完整的GC运行时冻结。 在一个多GB的服务器上,这很容易引起注意,这取决于如何configurationjvm,以及它有多less空间等等 – 我已经看到超过30秒的暂停。

另一个问题是,通过显式调用GC,你实际上并没有监视JVM如何运行GC,而是实际上改变了它 – 取决于你如何configurationJVM,它将在适当的时候进行垃圾回收,并且通常是递增的(当内存不足时,它不会运行完整的GC)。 你将要打印出来的东西不会像JVM自己做的那样 – 一方面你可能会看到更less的自动/增量GC,因为你将手动清除内存。

正如Nick Holt的post所指出的,打印GC活动的选项已经作为JVM标志存在。

你可以有一个线程,只是打印出一个合理的间隔免费和可用,这会告诉你实际的内存使用率。

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

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

它以可configuration的时间间隔提供原始信息,这对于logging和绘图目的非常有用。

如果你使用java 1.5,你可以看看ManagementFactory.getMemoryMXBean(),它给你所有types的内存数字。 堆和非堆,perm – gen。

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

如果使用JMX提供的GC运行历史logging,则可以使用前后相同的数字,而不必强制执行GC。

你只需要记住那些GC运行(通常是一个旧的和一个新的一代)不是常规的intervalls,所以你也需要提取开始时间以及绘图(或者你对一个序列号进行绘图,对于大多数实际的目的是足够的绘图)。

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

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

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

如上所述,尝试使用VisualVM来获得基本视图。

您也可以使用Eclipse MAT来做更详细的内存分析。

只要你不依赖于System.gc()就可以了,因为你的程序是正确的。

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

但是,如果你是在一个非常有限的内存条件下工作,比如移动设备,System.gc允许你手动分配更多的时间用于这个垃圾回收,但是要花费CPU时间(但是,正如你所说,你不关心gc的性能问题)。

最好的做法可能是只使用它,你可能会做大量的解除分配(如冲洗一个大型数组)。

所有的考虑因素,因为你只是关心内存使用情况,随时调用gc,或者更好的是,看看它是否在你的情况下有很大的记忆差异,然后决定。

添加到答案列表中,JConsole(包含在jdk / bin目录中)是一个很好的界面。

参考