5.3.5 选择收集器降低延迟

5.3.5 选择收集器降低延迟

现在Eclipse启动已经比较迅速了,但我们的调优实战还没有结束,毕竟Eclipse是拿来写程序用的,不是拿来测试启动速度的。我们不妨再在Eclipse中进行一个非常常用但又比较耗时的操作:代码编译。图5-11是当前配置下,Eclipse进行代码编译时的运行数据,从图中可以看到,新生代每次回收耗时约65毫秒,老年代每次回收耗时约725毫秒。对于用户来说,新生代垃圾收集的耗时也还好,65毫秒的停顿在使用中基本无法察觉到,而老年代每次垃圾收集要停顿接近1秒钟,虽然较长时间才会出现一次,但这样的停顿已经是可以被人感知了,会影响到体验。

再注意看一下编译期间的处理器资源使用状况,图5-12是Eclipse在编译期间的处理器使用率曲线图,整个编译过程中平均只使用了不到30%的处理器资源,垃圾收集的处理器使用率曲线更是几乎与坐标横轴紧贴在一起,这说明处理器资源还有很多可利用的余地。

image-20210919160657099

图5-11 编译期间运行数据

image-20210919160720782

图5-12 编译期间CPU曲线

列举垃圾收集的停顿时间、处理器资源富余的目的,都是为了给接下来替换掉客户端模式的虚拟机中默认的新生代、老年代串行收集器做个铺垫。

Eclipse应当算是与使用者交互非常频繁的应用程序,由于代码太多,笔者习惯在做全量编译或者清理动作的时候,使用“Run in Background”功能一边编译一边继续工作。回顾一下在第3章提到的几种收集器,很容易想到在JDK 6版本下提供的收集器里,CMS是最符合这类场景的选择。我们在eclipse.ini中再加入这两个参数,-XX:+UseConc-MarkSweepGC和-XX:+UseParNewGC(ParNew是使用CMS收集器后的默认新生代收集器,写上仅是为了配置更加清晰),要求虚拟机在新生代和老年代分别使用ParNew和CMS收集器进行垃圾回收。指定收集器之后,再次测试的结果如图5-13所示,与原来使用串行收集器对比,新生代停顿从每次65毫秒下降到了每次53毫秒,而老年代的停顿时间更是从725毫秒大幅下降到了36毫秒。

image-20210919160805841

图5-13 指定ParNew和CMS收集器后的GC数据

当然,由于CMS的停顿时间只是整个收集过程中的一小部分,大部分收集行为是与用户程序并发进行的,所以并不是真的把垃圾收集时间从725毫秒直接缩短到36毫秒了。在收集器日志中可以看到CMS与程序并发的时间约为400毫秒,这样收集器的运行结果就比较令人满意了。

到这里为止,对于虚拟机内存的调优基本就结束了,这次实战可以看作一次简化的服务端调优过程,服务端调优有可能还会在更多方面,如数据库、资源池、磁盘I/O等,但对于虚拟机内存部分的优化,与这次实战中的思路没有什么太大差别。即使读者实际工作中不接触到服务器,根据自己工作环境做一些试验,总结几个参数让自己日常工作环境速度有较大幅度提升也是很能提升工作幸福感的。 最终eclipse.ini的配置如代码清单5-12所示。

代码清单5-12 修改收集器配置后的Eclipse配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-vm 
D:/_DevSpace/jdk1.6.0_21/bin/javaw.exe
-startup
plugins/org.eclipse.equinox.launcher_1.0.201.R35x_v20090715.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.0.200.v20090519
-product
org.eclipse.epp.package.jee.product
-showsplash
org.eclipse.platform
-vmargs
-Dcom.sun.management.jmxremote
-Dosgi.requiredJavaVersion=1.5
-Xverify:none
-Xmx512m
-Xms512m
-Xmn128m
-XX:PermSize=96m
-XX:MaxPermSize=96m
-XX:+DisableExplicitGC
-Xnoclassgc
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=85