首页 文章

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

提问于
浏览
55

我们在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()是错误的,"let jvm decide when to run gc" . 基本上他的论点与disscussed here相同 . 但我还是不明白:

  • 如何获得内存使用曲线?

  • 显式gc()有什么问题?我不关心使用显式gc()可能发生的小的性能问题,我估计在1-3% . 我需要的是内存和线程监视器,它可以帮助我分析我们在客户现场的系统 .

13 回答

  • 2

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

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

  • 0

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

    调用System.gc()可能会损坏GC 's performance because objects will be prematurely moved from the new to old generations, and weak references will be cleared prematurely. This can result in decreased memory efficiency, longer GC times, and decreased cache hits (for caches that use weak refs). I agree with your consultant: System.gc() is bad. I' d使用命令行开关到disable it .

  • 4

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

    理论上,理论和实践都是一样的 . 实际上,他们不是 .

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

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

  • 9

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

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

    示例:
    JVM Memory Dashboard

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

    注意:我是stagemonitor的开发者

  • 0
  • 6

    看看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打印终身年龄信息 .

    虽然您不打算通过显式调用_1022296来打乱JVM,但它们可能没有您期望的效果 . 要真正了解JVM中的内存发生了什么,并且读取了Brian Goetz所写的所有内容 .

  • 5

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

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

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

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

  • 5

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

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

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

  • 28

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

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

  • 8

    如果您使用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实例中这样做 .

  • 9

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

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

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

  • 48

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

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

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

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

相关问题