2 应用启动优化代码后遇到的问题
1.1 优化后的性能反而比优化前差
- 问题场景:改造namenode启动时加载fsimage文件的逻辑,将串行加载改成了并行加载,并行加载启动了4个线程。但是最后测出来的结果却是优化前的耗时更短
- 原因:当时排查了内存、cpu、io,都不是瓶颈,又深入代码层面,模拟由于并行增加的加锁逻辑的耗时,发现即使争用锁会有一点性能消耗,但是无论如何也抵消不了并行带来的性能提升。后来发现了问题所在,我在查看cpu使用率时是通过prometheus采集到的jmx指标,但是忽略了namenode在重启时的cpu使用指标其实是采集不到的。通过top命令发现了cpu是瓶颈,通过调大cpu,优化后的性能显著上升,符合预期。
- 新问题:在排查过程中又发现了一个新问题,我通过"top -Hp namenode进程id"查看cpu使用最高的线程,记录下线程id,然后通过"jstack namenode进程id > stack.log"将堆栈信息导入satck.log文件中。最后发现其实并行加载线程总体占用的cpu资源不足总cpu占用资源的一半,而gc线程占用的cpu资源占了总cpu资源的一大半。通过查看gc日志,发现在namenode启动时频繁发生Young GC,基本每过0.3秒发生一次。
- 原因:为什么Young GC发生如此频繁?看了采用的垃圾收集器是G1,在集群代码中没有设置初始堆大小和新生代比例,那么默认的初始堆大小就是最大堆大小的1/64,新生代比例占堆大小的5%。导致开始时的新生代非常小,启动结束后加载到内存中的大小由12G,因此频繁发生Young GC也就可以理解了e
- 解决方案:加大cpu,修改jvm参数(调大初始堆内存和新生代比例)(待验证)