【问题标题】:How to determine which method is generating the most garbage如何确定哪种方法产生的垃圾最多
【发布时间】:2014-01-29 03:12:34
【问题描述】:

我有三个对象:

ClassA classA = new ClassA();
ClassA classB = new ClassA();
ClassA classC = new ClassA();

这些对象中的每一个都有一个名为runpublic void,它执行某些操作并以下列方式调用:

   classA.run();                                                                             
   classB.run();                                                                             
   classC.run();   

在每次循环迭代中,每个run 调用都会生成特定数量的垃圾。

如何准确判断每个run产生的垃圾量?

【问题讨论】:

  • 您无法控制垃圾收集。 finalize()、runFinalizersOnExit() 等不能保证运行。为什么不把更精细的细节留给 JVM?
  • 从某种意义上说,我确实可以控制垃圾收集——我可以看到产生的垃圾最多,然后减少产生的垃圾。这是我的目标,
  • 但为什么这是你的目标?这听起来像是过早的优化。
  • @Gimby 对于一个不希望 GC 停止世界的实时系统......
  • 限制垃圾数量只会延迟 GC,但它最终会发生,您无法确定何时发生。为了解决这个问题,你应该看看像 Zing 这样的实时 JVM - azulsystems.com/products/zing/virtual-machine - 它使用他们的 C4 Continuously Concurrent Compacting Collector 来完全避免 GC 暂停。

标签: java memory garbage-collection benchmarking heap-memory


【解决方案1】:

通常(如果您可以对 OP 的用例这么说),您使用分析器/日志记录来估计分配压力。例如,JRockit Mission Control(适用于 JDK 7u40!)可以转储当前的分配率。连续执行run,可以估计每个run 调用的分配率是多少。确保您严格衡量run 呼叫率,因为此时您正在处理基准测试。以jmh 为例。

然而,这很无聊。对于完全不同的东西,我有一个棘手(但很有趣!)的建议。由于在包括 HotSpot 在内的大多数现代 VM 中,新对象在线程本地分配缓冲区 (TLAB) 中线性分配,因此有可能获得一个地址标记,执行run(),然后获得另一个地址标记,减去两个标记,然后获取调用期间在 TLAB 中浪费的空间量。

事实上,在jol,我们有the funny example doing almost exactly this thing

【讨论】:

    【解决方案2】:

    有一个鲜为人知的 JVM 选项 -Xaprof 报告每个类的全局分配总数。我们创建了一个内部开源“aprof”工具,可以报告每个类的每种方法的分配率:https://code.devexperts.com/display/AProf/

    它通过即时修改字节码来执行对象分配跟踪,并尝试平衡性能影响与其报告的深度和精度。它确实显着增加了应用程序的启动时间,但对运行时性能的影响可以忽略不计,因此我们可以在生产环境中的服务器端组件上使用它。

    Aprof 精确收集代码中的所有“新”操作,但不收集每个分配的完整堆栈跟踪。它跟踪每个分配操作的“位置”(类名和方法名),同时提供一种方法来将 char[] 的分配(例如 StringBuilder 方法)归因于调用相应 StringBuilder 方法的实际应用程序代码.它为所有流行的 java.lang 和 java.util 类提供了一组合理的默认值,这些类确实在它们内部分配内存,因此您可以将实际的应用程序类视为这些分配的最终位置来源。

    【讨论】:

      【解决方案3】:

      您可以尝试使用内置分析器VisualVM 来查看细节。

      您可以在您的 JDK/bin 文件夹中以 jvisualvm.exe 或类似名称找到它。

      【讨论】:

      • 我如何使用 VisualVM 来完成这项任务?
      • 一旦你启动它,只需从左边的列表中选择你的应用程序正在运行的java进程(你可能需要通过任务管理器或你有什么来查找它的PID),打开它,您应该会看到各种选项卡,例如 Monitoring/Threads/Sampler/Profiler、GC 活动和 Monitoring 中的堆大小,将向您展示您可能感兴趣的事物的总体视图,但您应该使用采样器或分析器(可能profiler 更准确)查看哪些方法/类在 CPU 时间的持续时间内使用了多少内存。
      【解决方案4】:
      double[] heapFreeSizez = new double[5];
      for(int i = 0 ; i < 5 ; ++i) {
          heapFreeSizez[i] = 0;
      }
      
      heapFreeSizez[0] = (double) Runtime.getRuntime().freeMemory()/1000000.0;
      classA.run(); heapFreeSizez[1] = (double) Runtime.getRuntime().freeMemory()/1000000.0;
      classB.run(); heapFreeSizez[2] = (double) Runtime.getRuntime().freeMemory()/1000000.0;
      classC.run(); heapFreeSizez[3] = (double) Runtime.getRuntime().freeMemory()/1000000.0;
      classD.run(); heapFreeSizez[4] = (double) Runtime.getRuntime().freeMemory()/1000000.0;
      
      for(int i = 1 ; i < 5 ; ++i) {
          if(heapFreeSizez[i] - heapFreeSizez[i-1] < 0) {
      
              System.out.println(i + "_" + "HELP!"); 
          }
      }
      

      【讨论】:

        【解决方案5】:

        你能用ThreadMXBean#getThreadAllocatedBytes吗? 它告诉您特定线程分配的内存量。 您需要做的只是测量差异(并考虑getThreadAllocatedBytes 本身产生的一些 8-16 字节垃圾)

        【讨论】:

          猜你喜欢
          • 2014-03-07
          • 1970-01-01
          • 1970-01-01
          • 2021-12-28
          • 2011-02-24
          • 2015-05-24
          • 2011-07-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多