【问题标题】:How to monitor Java memory usage?如何监控 Java 内存使用情况?
【发布时间】:2009-06-29 15:41:19
【问题描述】:

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

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

此代码运行良好。这意味着它显示了与现实相对应的记忆曲线。当我们从数据库创建一个大的 xml 文件时,曲线上升,提取完成后曲线下降。

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

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

【问题讨论】:

  • 用 jvisualvm 监控 - 提供更好的统计数据。

标签: java garbage-collection monitoring memory-management


【解决方案1】:

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

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

【讨论】:

  • 刚刚在看 VisualVM - 很不错。
  • System.gc() 会损害性能,因为它会干扰 GC 的算法。这会降低所有后续的 GC。
  • [需要引用] 想证明您的主张?请解释对 gc() 的调用如何“干扰”垃圾收集器算法以及对方法的调用如何“降级”后续调用?
  • 我用过可视化虚拟机,效果很好。它显示了服务器内部的很多内部细节。
  • VM 有一个 -XX:+DisableExplicitGC 选项是有原因的!问题不在于调用 GC 一定不好。但是如果您过于频繁地调用 GC 可能会很糟糕。当大多数算法不会停止整个 VM 进行完整 GC 时,它可能会导致完整 GC。如果您正在运行一个 HUGE,这可能会特别成问题,整个 VM 可能会暂停几分钟。如果发生这种情况,您可能会导致其他操作超时(如数据库连接、rmi 连接等)
【解决方案2】:

有一些工具可以让您监控虚拟机的内存使用情况。 VM can expose memory statistics using JMX。您还可以print GC statistics 来查看内存在一段时间内的表现。

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

【讨论】:

    【解决方案3】:

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

    您可以选择使用出色的时间序列数据库石墨来存储您可以通过精美的仪表板查看的长期数据点历史记录。

    示例:

    查看project website 以查看屏幕截图、功能描述和文档。

    注意:我是stagemonitor的开发者

    【讨论】:

      【解决方案4】:

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

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

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

      虽然理论上您可能会弄乱一些经过微调的 JVM 垃圾收集实现,除非您正在编写旨在部署在任何 JVM 上的通用代码,否则不要担心。如果它适合你,那就去做吧。

      【讨论】:

      • 你知道这种行为只是产生一个线程并立即运行是否也适用于当前的(Sun/Oracle/Hotspot)JVM(8)?
      • 经常运行 System.gc,只是为了获得内存使用的合理统计数据,损害性能。所以这不是“只做”。 System.gc() 在“好的,现在一项需要大内存的大工作完成了,我现在没什么可做的,所以我收集垃圾以提高以后小垃圾收集的性能”这样的情况是明智的。跨度>
      【解决方案5】:

      【讨论】:

      【解决方案6】:

      通过 Visual VM 了解 tomcat 内部发生的情况。 http://www.skill-guru.com/blog/2010/10/05/increasing-permgen-size-in-your-server/

      【讨论】:

        【解决方案7】:

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

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

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

        您可以有一个线程以合理的时间间隔免费打印出来,这将向您显示实际的内存使用情况。

        【讨论】:

        • 不调用 System.gc() 如何缓解这种情况?最终,当 JVM 内存不足时,它会收集垃圾,那么延迟不会发生吗? (至少这是我的经验)。
        • JVM 允许您从多个 GC 算法中指定。他们都进行某种增量 GC,并且可能偶尔会触发完整的 GC(我不太了解细节,但基本上只有在他们必须这样做时——某种“最后手段”之类的事情,例如,当没有有足够的空间在增量 gc 期间制作 mem 某些部分的新副本)。如果您经常调用 System.gc,它会更频繁地发生。您实际上是在创建自己的 GC 策略,即“只是定期运行完整的 GC”。 JVM 有比这更好的策略。
        【解决方案8】:

        看看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,但它们可能不会达到您期望的效果。通过读取 Brian Goetz 所写的所有内容,真正了解 JVM 中的内存发生了什么。

        【讨论】:

          【解决方案9】:

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

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

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

          【讨论】:

            【解决方案10】:

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

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

            【讨论】:

              【解决方案11】:

              如果您使用 JMX 提供的 GC 运行历史记录,您可以使用相同的前后编号,您不必强制 GC。

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

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

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

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

              【讨论】:

                【解决方案12】:

                按照建议,尝试 VisualVM 以获得基本视图。

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

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

                【讨论】:

                  【解决方案13】:

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

                  但是,例如,如果您在内存非常有限的条件下工作(例如移动设备),System.gc 允许您手动分配更多时间来进行垃圾回收,但代价是 CPU 时间(但是,正如你所说,你并不关心 gc 的性能问题)。

                  最佳实践可能是仅在您可能进行大量解除分配(如刷新大型数组)的情况下使用它。

                  综合考虑,由于您只关心内存使用情况,请随时调用 gc,或者更好的是,看看它是否对您的情况有很大的内存差异,然后再决定。

                  【讨论】:

                    【解决方案14】:

                    关于 System.gc()... 我刚刚在 Oracle 的文档中读到下面这句话 here

                    显式垃圾回收的性能影响可以通过使用标志 -XX:+DisableExplicitGC 禁用它们来衡量,这会导致 VM 忽略对 System.gc() 的调用。

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

                    还要注意前面引用的句子前面是这个:

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

                    【讨论】:

                      【解决方案15】:

                      JavaMelody 可能是满足您需求的解决方案。

                      该工具专为 Java EE 应用程序开发,可测量和构建有关应用程序在任何环境中的实际运行情况的报告。它是免费和开源的,易于集成到具有一些历史记录的应用程序中,无需数据库或分析,非常轻量级。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2013-11-29
                        • 2016-10-05
                        • 2011-04-30
                        • 2011-07-10
                        • 1970-01-01
                        • 2021-11-01
                        • 2012-03-29
                        • 1970-01-01
                        相关资源
                        最近更新 更多