繁体   English   中英

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

[英]How to monitor Java memory usage?

我们有一个在 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”。 基本上他的论点与这里讨论的相同。 但我还是不明白:

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

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

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

有一些工具可让您监控 VM 的内存使用情况。 VM 可以使用 JMX 公开内存统计信息 您还可以打印 GC 统计信息以查看内存随时间的运行情况。

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

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

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

例子:JVM 内存仪表板

查看项目网站以查看屏幕截图、功能描述和文档。

注:我是stagemonitor的开发者

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

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

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

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

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

替代文字

看看 JVM 参数: 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 写的所有内容和所有内容。

在生产系统上显式运行 System.gc() 是一个糟糕的主意。 如果内存达到任何大小,整个系统可能会在 full GC 运行时冻结。 在一个多 GB 大小的服务器上,这很容易变得非常明显,这取决于 jvm 的配置方式,以及它有多少净空等等——我见过超过 30 秒的停顿。

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

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

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

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

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

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

如果您使用 java 1.5,您可以查看 ManagementFactory.getMemoryMXBean() ,它为您提供各种内存的数字。 堆和非堆,永久代。

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

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

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

例如,在带有 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 实例中这样做。

正如所建议的,尝试 VisualVM 以获得基本视图。

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

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

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

但是,例如,如果您在内存非常有限的情况下工作,例如移动设备,System.gc 允许您手动为此垃圾收集分配更多时间,但以 CPU 时间为代价(但是,正如您所说,您并不那么关心 gc 的性能问题)。

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

考虑到所有因素,因为您只关心内存使用情况,请随时调用 gc,或者更好的是,看看它是否对您的情况产生很大的内存差异,然后再决定。

关于System.gc()的......我刚才读的Oracle文档下面的语句在这里

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM