使用英特尔性能计数器调整垃圾收集

介绍

我不得不承认我很震惊。 确实,当我意识到这个出现的日历帖子将涉及垃圾收集时,我感到非常震惊。 GC的主题引起了Java倡导者那些认为内存管理应该是手动的人的热情。 撰写了许多文章,内容涉及看起来奇怪的命令行参数中的细微变化,这些变化对Java应用程序的性能影响很小。 我该如何增加这项庞大的身体工作?

我希望这篇文章不会增加GC的热度,而是让您呼吸新鲜空气。 让我们不要看垃圾回收器消耗的CPU时间或暂停时间; 如何看待内存管理的一个隐藏但潜在的关键方面,尤其是垃圾回收:数据缓存是现代计算机软件设计的主要挑战之一(其他是指令缓存和多核工作分配)。

现代CPU的运行速度如此之快,以至于主内存无法跟上。 可以减轻这种灾难性性能损失中的一部分的方式。 内存被并行拉入高速缓存,然后CPU访问该缓存。 如果我们很幸运,并且代码使CPU几次读取和写入相同的内存(例如在循环内),则CPU可以愉快地访问高速缓存,并且可以免于等待加载和存储到主内存或从主内存中存储。

人们可能会问: “垃圾回收如何影响缓存的性能 ?” 有很多方法,其中有些非常微妙,但是,这里有一些重要的方法:

垃圾收集器遍历内存中的引用。 这导致高速缓存行(高速缓存中的内存块)包含引用周围的内存,因此不再保存程序正在使用的其他数据。

虽然我们将其称为垃圾收集器,但实际上它是分配器,移动器和收集器。 当我们考虑数据缓存时,这确实很重要:

  • 分配:根据硬件规则,内存地址与高速缓存行匹配。 如果内存共享一条高速缓存行,但实际上是从不同的线程访问的,我们会得到一种称为假共享的效果。 但是,如果少量数据散布但从同一线程访问,则缓存利用率很低。
  • 移动:对象在整个生命周期中都不会留在一处。 垃圾收集器通过移动对象来避免内存碎片。 这具有有趣的效果,可以保证与对象关联的高速缓存行在移动后不再与之关联。
  • 收集:有趣的是,收集是一件容易的事。 它可以很简单,只需将内存标记为可重复使用即可。 对象图(多个根)的遍历是为了找出可以收集的内容,这将导致数据高速缓存行负载,从而从用户代码读取或写入的数据中删除高速缓存行。

因此,我们现在可以看到垃圾收集器的设计对于数据缓存的运行至关重要。 交换我们使用的收集器不仅会影响GC暂停和其他明显的问题,而且还会从根本上影响所有用户代码。

一个例子

我不会就此概念发表详尽的科学论文。 这篇文章的目的是展示一种进行JVM调优的替代方法。 因此,我在个人合成器程序Sonic-Field中运行了一个简单,简短,多线程的补丁程序。 该补丁使用反馈,共鸣和其他一些概念来合成弦乐器,然后进行卷积以将声音放置在声学环境中。

选择音场的原因不仅是因为它具有合理的复杂性,高度线程化并使用Spring,还因为我最近发现我可以使用CMS垃圾收集器从中获得更好的性能。 与Sonic-Field的延迟无关紧要,因为它是批处理处理器。 但是,当内存不足时,标准Java 7垃圾收集器与Sonic Field将交换文件写出到磁盘的方式交互不良。 我之所以尝试CMS,是因为它使内存一直处于关闭状态(理论上,不要让我失望),因为它一直在尝试在用户线程旁边进行小的垃圾回收。

如果将所有这些放在一起,我们很可能会提出一个合理的理论
“ CMS垃圾收集器可能会减少暂停时间,也许能够减少内存使用量,但这样做几乎肯定会导致更多数据高速缓存未命中” 。 不断遍历内存中的参考图以尝试收集死对象将导致高速缓存加载,而这些加载将导致从高速缓存中清除其他数据(其大小有限)。 因此,当用户线程再次读取时,它们将导致更多的高速缓存未命中,依此类推。

有关系吗? 答案将完全取决于应用程序,硬件以及应用程序的负载。 我不是,我不再重复,提倡一个垃圾收集器而不是另一个! 尽管如此,这是我想回答的一个问题,所以让我们为我的小测试补丁回答它。

垃圾收集器的这些数据缓存效果在普通的VM分析工具中是不可见的。 这意味着它们在JVM社区中没有得到太多讨论,并且在JVM调优中得到的考虑甚至更少。 但是,有一个工具(实际上是几个,但我将讨论最简单的使用方法)可以使您对该主题有所了解。 我说的是英特尔的PCM(性能计数器监视器)。 它也可以用于代码调整,但我认为今天谈论GC会更有趣。

一个可行的例子

pcm只是命令行工具。 我们通过命令行将引号引起来运行Java并对其进行测量。 通过其他工具,性能计数器可用于获取有关应用程序的各种其他详细信息。 pcm命令行工具的优点在于它的简单性,并且不会干扰整个应用程序的运行。 缺点是它将衡量JVM和应用程序预热阶段。 但是,对于服务器样式的应用程序或批处理处理器(例如Sonic Field),与实际应用程序运行相比,这些开销通常是微不足道的。

我在具有16Gig RAM的个人Macbook Pro Retina(2012)上运行了补丁。 JVM是:

java version "1.8.0-ea"

Java(TM) SE Runtime Environment (build 1.8.0-ea-b61)

Java HotSpot(TM) 64-Bit Server VM (build 25.0-b05, mixed mode)

当应用程序退出时,从pcm读取的数据将被简单地写入标准输出。 我将运行时没有设置垃圾收集器的设置(因此是默认设置)与当前首选的调整进行了比较。 老实说,我不确定这些调整是否最佳; 我从一系列在线文章中解脱了他们……

这是Java的启动脚本:

/Users/alexanderturner/x/IntelPerformanceCounterMonitorV2.5.1\ 2/pcm.x "java \

 -Xmx12G -Xms12G  -DsonicFieldTemp=/Users/alexanderturner/temp -DsonicFieldThreads=12 -DsonicFieldSwapLimit=4.0  -XX:+UseConcMarkSweepGC -XX:+UseCompressedOops -XX:ParallelGCThreads=8 -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseCMSInitiatingOccupancyOnly \

 -classpath \
bin:\

ing-framework-3.1.2.RELEASE/dist/org.springframework.asm-3.1.2.RELEASE.jar:\
spring/sp
spring/sp
rring-framework-3.1.2.RELEASE/dist/org.springframework.beans-3.1.2.RELEASE.jar:\

ing-framework-3.1.2.RELEASE/dist/org.springframework.core-3.1.2.RELEASE.jar:\
spring/sprin
spring/spring-framework-3.1.2.RELEASE/dist/org.springframework.context-3.1.2.RELEASE.jar:\
spring/spring-framework-3.1.2.RELEASE/dist/org.springframework.context-support-3.1.2.RELEASE.jar:\
spring/sp
rg-framework-3.1.2.RELEASE/dist/org.springframework.expression-3.1.2.RELEASE.jar:\
spring/spring-framework-3.1.2.RELEASE/dist/org.springframework.test-3.1.2.RELEASE.jar:\
spring/otherJars/commons-logging-1.1.1.jar \
    com.nerdscentral.sfpl.RenderRunner $1

"

希望可以清楚地知道,在IntelPerformanceCounterMonitorV2下运行Java到底有多简单。 因此,这是输出:

标准GC

Core (SKT) | EXEC | IPC  | FREQ  | AFREQ | L3MISS | L2MISS | L3HIT | L2HIT | L3CLK | L2CLK  | READ  | WRITE | TEMP

   0    0     0.53   0.75   0.70    1.31     422 M    621 M    0.32    0.32    0.14    0.01     N/A     N/A     32

  2    0     0.56   0.77   0.73    1.31     346 M    466 M    0.26    0.31    0.11    0.01     N/A     N/A     28

   1    0     0.22   0.69   0.32    1.31     144 M    192 M    0.25    0.28    0.11    0.01     N/A     N/A     32

    3    0     0.21   0.68   0.31    1.31     135 M    171 M    0.21    0.28    0.10    0.01     N/A     N/A     28
4    0     0.55   0.77   0.71    1.31     332 M    410 M    0.19    0.38    0.11    0.01     N/A     N/A     22

  7    0     0.18   0.68   0.26    1.30     124 M    134 M    0.08    0.30    0.11    0.00     N/A     N/A     27

   5    0     0.19   0.68   0.29    1.31     133 M    155 M    0.14    0.30    0.11    0.00     N/A     N/A     22
   6    0     0.61   0.79   0.78    1.32     343 M    382 M    0.10    0.35    0.10    0.00     N/A     N/A     27

 -------------------------------------------------------------------------------------------------------------------
 SKT    0     0.38   0.75   0.51    1.31    1982 M   2533 M    0.22    0.33    0.11    0.01     N/A     N/A     22

 Instructions retired: 2366 G ; Active cycles: 3166 G ; Time (TSC):  773 Gticks ; C0 (active,non-halted) core resi
-------------------------------------------------------------------------------------------------------------------
 TOTAL  *     0.38   0.75   0.51    1.31    1982 M   2533 M    0.22    0.33    0.11    0.01     N/A     N/A     N/A
dency: 39.04 %

C                 : 1.49 => corresponds to 37.36 % utilization for cores in active state
 Instructions per no
 C1 core residency: 23.92 %; C3 core residency: 0.01 %; C6 core residency: 0.00 %; C7 core residency: 37.02 %
 C2 package residency: 0.00 %; C3 package residency: 0.00 %; C6 package residency: 0.00 %; C7 package residency: 0.00 %

 PHYSICAL CORE I
Pminal CPU cycle: 0.76 => corresponds to 19.12 % core utilization over time interval

并发标记扫描

而不是

-XX:+UseConcMarkSweepGC 

  -XX:+UseCompressedOops

  -XX:+CMSParallelRemark

  -XX:ParallelGCThreads=

8Enabled

  -XX:CMSInitiatingOccupancyFraction=60

  -XX:+UseCMSInitiatingOccupancyOnly
Core (SKT) | EXEC | IPC  | FREQ  | AFREQ | L3MISS | L2MISS | L3HIT | L2HIT | L3CLK | L2CLK  | READ  | WRITE | TEMP

   0    0     0.53   0.69   0.76    1.31     511 M    781 M    0.35    0.35    0.17    0.02     N/A     N/A     26

  2    0     0.54   0.71   0.75    1.31     418 M    586 M    0.29    0.40    0.14    0.01     N/A     N/A     29

   1    0     0.31   0.66   0.47    1.30     207 M    285 M    0.27    0.26    0.11    0.01     N/A     N/A     26

    3    0     0.30   0.66   0.46    1.30     198 M    258 M    0.23    0.27    0.11    0.01     N/A     N/A     29
4    0     0.59   0.73   0.81    1.31     397 M    504 M    0.21    0.46    0.12    0.01     N/A     N/A     29

  7    0     0.30   0.66   0.45    1.30     186 M    204 M    0.09    0.29    0.11    0.00     N/A     N/A     30

   5    0     0.30   0.66   0.45    1.30     188 M    225 M    0.16    0.28    0.11    0.01     N/A     N/A     29
   6    0     0.58   0.73   0.79    1.31     414 M    466 M    0.11    0.49    0.13    0.00     N/A     N/A     30

 -------------------------------------------------------------------------------------------------------------------
 SKT    0     0.43   0.70   0.62    1.31    2523 M   3313 M    0.24    0.38    0.13    0.01     N/A     N/A     25

 Instructions retired: 2438 G ; Active cycles: 3501 G ; Time (TSC):  708 Gticks ; C0 (active,non-halted) core resi
-------------------------------------------------------------------------------------------------------------------
 TOTAL  *     0.43   0.70   0.62    1.31    2523 M   3313 M    0.24    0.38    0.13    0.01     N/A     N/A     N/A
dency: 47.22 %

C                 : 1.39 => corresponds to 34.83 % utilization for cores in active state
 Instructions per no
 C1 core residency: 17.84 %; C3 core residency: 0.01 %; C6 core residency: 0.01 %; C7 core residency: 34.92 %
 C2 package residency: 0.00 %; C3 package residency: 0.00 %; C6 package residency: 0.00 %; C7 package residency: 0.00 %

 PHYSICAL CORE I 
Pminal CPU cycle: 0.86 => corresponds to 21.51 % core utilization over time interval

这里提供的所有信息都很有趣,但是我想做的最好的事情就是减少案例并测试我对CMS收集器的主张。 为此,我们只需要看两行即可得出每次运行的输出:

Default:

SKT    0     0.38   0.75   0.51    1.31    1982 M   2533 M    0.22    0.33    0.11    0.01     N/A     N/A     22

Instructions retired: 2366 G ; Active cycles: 3166 G ; Time (TSC):  773 Gticks ; C0 (active,non-halted) core residency: 39.04 %

CMS:

0     0.43   0.70   0.62    1.31    2523 M   3313 M    0.24    0.38    0.13    0.01     N/A     N/A     25

SKT 

Instructions retired: 2438 G ; Active cycles: 3501 G ; Time (TSC):  708 Gticks ; C0 (active,non-halted) core residency: 47.22 %

讨论区

我们可以看到,在CMS收集器下,缓存未命中率大大提高。 与默认收集器相比,L2遗漏增加了30%,L2遗漏了27%。 但是,以十亿分s为单位的总时间(708CMS / 773默认)表明,所有这些额外的数据丢失并没有对所有性能产生负面影响。 我想这意味着在就此应用程序的正确方法得出任何结论之前,可以而且应该做更多的研究!

如果您以我未充分讨论该主题为由而离开,则您是正确的。 我的目的是使读者对Java性能的这一方面感兴趣并为新方法打开大门。

参考:Java Advent Calendar博客上,可以使用JCG合作伙伴 Attila Mihaly Balazs的英特尔性能计数器来调优垃圾回收

翻译自: https://www.javacodegeeks.com/2013/12/using-intel-performance-counters-to-tune-garbage-collection.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值