如何监视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指标,请求详细信息(包括由请求分析器捕获的调用堆栈)等。 开销很低。
或者,您可以使用伟大的时间序列数据库石墨来存储数据点的悠久历史,您可以使用精美的仪表板查看这些数据点。
例:
查看项目网站 ,查看截图,function描述和文档。
注意:我是Stagemonitor的开发者
你有没有试过JMX?
http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html
替代文字http://java.sun.com/developer/technicalArticles/J2SE/jconsole/HeapMemory.jpg
看看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
,绝对startTime
和memoryUsageBefore
和memoryUsageAfter
。
只需使用一个计时器来读取该属性。 每当出现一个新的startTime时,就会知道它描述了一个新的GC事件,您将提取内存信息并继续轮询下一次更新。 (不知道是否可以使用AttributeChangeNotification
。)
提示:在你的计时器中,你可以测量到上一次GC运行的距离,如果你的绘图的时间太长,你可以有条件的调用System.gc()。 但是我不会在OLTP实例中这样做。
如上所述,尝试使用VisualVM来获得基本视图。
您也可以使用Eclipse MAT来做更详细的内存分析。
只要你不依赖于System.gc()就可以了,因为你的程序是正确的。
system.gc的问题是JVM已经根据内存使用情况自动为垃圾收集器分配时间。
但是,如果你是在一个非常有限的内存条件下工作,比如移动设备,System.gc允许你手动分配更多的时间用于这个垃圾回收,但是要花费CPU时间(但是,正如你所说,你不关心gc的性能问题)。
最好的做法可能是只使用它,你可能会做大量的解除分配(如冲洗一个大型数组)。
所有的考虑因素,因为你只是关心内存使用情况,随时调用gc,或者更好的是,看看它是否在你的情况下有很大的记忆差异,然后决定。
添加到答案列表中,JConsole(包含在jdk / bin目录中)是一个很好的界面。
参考