首先警告 —— 不要认为分析器适用于所有的场景。分析器有时确实作用很大, 比如检测代码中的CPU热点时。但某些情况使用分析器不一定是个好方案。

    对GC调优来说也是一样的。要检测是否因为GC而引起延迟或吞吐量问题时, 不需要使用分析器. 前面提到的工具( jstat 或 原生/可视化GC日志)就能更好更快地检测出是否存在GC问题. 特别是从生产环境中收集性能数据时, 最好不要使用分析器, 因为性能开销非常大。

    如果确实需要对GC进行优化, 那么分析器就可以派上用场了, 可以对 Object 的创建信息一目了然. 换个角度看, 如果GC暂停的原因不在某个内存池中, 那就只会是因为创建对象太多了。 所有分析器都能够跟踪对象分配(via allocation profiling), 根据内存分配的轨迹, 让你知道 实际驻留在内存中的是哪些对象

    分配分析能定位到在哪个地方创建了大量的对象. 使用分析器辅助进行GC调优的好处是, 能确定哪种类型的对象最占用内存, 以及哪些线程创建了最多的对象。

    下面我们通过实例介绍3种分配分析器: hprof, JVisualVMAProf。实际上还有很多分析器可供选择, 有商业产品,也有免费工具, 但其功能和应用基本上都是类似的。

    内置于JDK之中。 在各种环境下都可以使用, 一般优先使用这款工具。

    要让 hprof 和程序一起运行, 需要修改启动脚本, 类似这样:

    在程序退出时,会将分配信息dump(转储)到工作目录下的 java.hprof.txt 文件中。使用文本编辑器打开, 并搜索 “SITES BEGIN” 关键字, 可以看到:

    1. SITES BEGIN (ordered by live bytes) Tue Dec 8 11:16:15 2015
    2. percent live alloc'ed stack class
    3. rank self accum bytes objs bytes objs trace name
    4. 1 64.43% 4.43% 8370336 20121 27513408 66138 302116 int[]
    5. 2 3.26% 88.49% 482976 20124 1587696 66154 302104 java.util.ArrayList
    6. ... 部分省略 ...
    7. SITES END

    现在, 知道有 64.43% 的对象是整数数组, 在 ClonableClass0006 类的构造函数中, 第11行的位置, 接下来就可以优化代码, 以减少GC的压力。

    本章前面的第一部分, 在监控 JVM 的GC行为工具时介绍了 JVisualVM , 本节介绍其在分配分析上的应用。

    JVisualVM 通过GUI的方式连接到正在运行的JVM。 连接上目标JVM之后 :

    1. 打开 “工具” —> “选项” 菜单, 点击 性能分析(Profiler) 标签, 新增配置, 选择 Profiler 内存, 确保勾选了 “Record allocations stack traces”(记录分配栈跟踪)。
    2. 勾选 “Settings”(设置) 复选框, 在内存设置标签下,修改预设配置。
    3. 点击 “Memory”(内存) 按钮开始进行内存分析。
    4. 让程序运行一段时间,以收集关于对象分配的足够信息。
    5. 单击下方的 “Snapshot”(快照) 按钮。可以获取收集到的快照信息。

    完成上面的步骤后, 可以得到类似这样的信息:

    分析器(Profilers) - 图2

    上图按照每个类被创建的对象数量多少来排序。看第一行可以知道, 创建的最多的对象是 int[] 数组. 鼠标右键单击这行, 就可以看到这些对象都在哪些地方创建的:

    最重要的一款分析器,是由 Devexperts 开发的 AProf。 内存分配分析器 AProf 也被打包为 Java agent 的形式。

    用 AProf 分析应用程序, 需要修改 JVM 启动脚本,类似这样:

      重启应用之后, 工作目录下会生成一个 aprof.txt 文件。此文件每分钟更新一次, 包含这样的信息:

      上面的输出是按照 size 进行排序的。可以看出, 80.44% 的 bytes 和 的 objects 是在 ManyTargetsGarbageProducer.newRandomClassObject() 方法中分配的。 其中, int[] 数组占用了 40.19% 的内存, 是最大的一个。

      继续往下看, 会发现 allocation traces(分配痕迹)相关的内容, 也是以 allocation size 排序的:

      1. Top allocated data types with reverse location traces
      2. ------------------------------------------------------------------------------------------------------------------------
      3. int[]: 725,306,304 (40.98%) bytes in 1,954,234 (7.85%) objects (avg size 371 bytes)
      4. eu.plumbr.demo.largeheap.ClonableClass0006.: 38,357,696 (2.16%) bytes in 92,206 (0.37%) objects (avg size 416 bytes)
      5. java.lang.reflect.Constructor.newInstance: 38,357,696 (2.16%) bytes in 92,206 (0.37%) objects (avg size 416 bytes)
      6. eu.plumbr.demo.largeheap.ManyTargetsGarbageProducer.newRandomClassObject: 38,357,280 (2.16%) bytes in 92,205 (0.37%) objects (avg size 416 bytes)
      7. java.lang.reflect.Constructor.newInstance: 416 (0.00%) bytes in 1 (0.00%) objects (avg size 416 bytes)
      8. ... cut for brevity ...

      可以看到, int[] 数组的分配, 在 ClonableClass0006 构造函数中继续增大。

      和其他工具一样, 揭露了 分配的大小以及位置信息(allocation size and locations), 从而能够快速找到最耗内存的部分。在我们看来, AProf 是最有用的分配分析器, 因为它只专注于内存分配, 所以做得最好。 当然, 这款工具是开源免费的, 资源开销也最小。

      原文链接: