【问题标题】:Getting the average size of a specific class from a running JVM从正在运行的 JVM 获取特定类的平均大小
【发布时间】:2020-02-16 13:32:15
【问题描述】:

我正在尝试获取正在运行的 JVM 中某个类的所有实例的平均大小。我可以使用 jcmd 或类似工具创建堆转储,但这需要几秒钟,这是一个生产服务器,所以我宁愿有更快的东西。 jcmd 可以选择创建这种格式的直方图:

 num     #instances         #bytes  class name
----------------------------------------------
   1:         84907      559415720  [I
   2:       9572537      229740888  java.lang.String
   3:        803323      142900392  [C
   4:       3190710      102102720  java.util.Hashtable$Entry

这似乎很有希望,但我认为字节大小是浅的而不是保留的内存。有没有办法创建具有保留大小的相同直方图?或者是另一种快速获取类实例平均大小的方法?

我知道 GDB 方法,但它不适合,因为输入命令序列需要几秒钟,而这大约是使用 jcmd 进行堆转储所需的时间。

【问题讨论】:

  • 假设您用 1 兆字节的文本填充 ArrayList<String>,然后用 list2.addAll(list1); 填充另一个列表。这两个列表的大小是多少?两种尺寸的总和是总尺寸吗?不,因为它们引用了相同的字符串。因此,不可能正确计算对象的大小。这在很大程度上取决于您要计算内存的哪些部分。

标签: java heap-dump jcmd


【解决方案1】:

我认为报告对象“深度大小”直方图的通用工具在技术上是不可行的。

为什么不呢?

如果对象的“深度大小”取决于其类的语义,我们就会遇到通用工具不知道对象边界在哪里的问题。可以实现一种工具,该工具具有诸如StringStringBufferHashMap 等标准类型的硬编码知识。然而:

  • 这种方法不能扩展,也不能处理应用程序和第 3 方库类。
  • 对于某些类型(例如集合类型),语义边界(即用户想要查找的内容)可能取决于上下文。

或者,如果一个对象的“深度大小”只是从它可以到达的所有对象的大小的总和,那么有两个问题:

  • 会出现明显的过度计数。例如,字符串的大部分内存使用都在字符数组中。这些数组将被计算两次:一次在String 对象的可达性图中,第二次作为字符数组。对于复杂的对象,过度计数可能会导致度量无用。

  • 计算直方图的平均值会非常昂贵。一种简单的方法是对堆中的每个对象执行可达性图遍历。不知道有没有更快的选择。

  • 另一种方法是通过在实例计数中不包括“内部”对象来避免重复计数。不幸的是,您会遇到同样具有误导性的现象,即(例如)更复杂对象中的内部字符串、集合等会消失。这意味着您无法再准确估计它们的平均尺寸。

我不认为有一个实际可行的计数方案。


话虽如此,您当前使用的(OpenJDK)工具的源代码可供下载。您可以修改工具以不同方式分析对象大小。但我不认为它会在开发工作量或修改工具的速度方面“快速”。

【讨论】:

  • jol 可以提供大小和深度大小,但不能在正在运行的 VM 中提供
猜你喜欢
  • 1970-01-01
  • 2011-07-16
  • 2021-12-24
  • 2021-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多