作者:张松然
简介:张松然,京东集团商家研发部架构师。2013年加入京东,具有10年软件开发和设计经验,以及丰富的构建高性能高可用大规模分布式系统的研发、架构经验。
疑惑
近期收到一些报警,是方法性能报警,定位原因主要是瞬时流量突增引起的,但是观察方法中查询 MySQL 的性能不差,性能花费在哪里?同时观察 JVM 性能,发现 YoungGC 变多了,CPU 使用率高了,但线程数在当时刻降低了,YoungGC 会创建新线程进行垃圾回收,应该线程数增多,为什么会降低的?
解惑
性能为什么慢了?
由于提供服务的 API 所依赖的 MySQL 性能并不慢,那原因是什么?唯一的问题点就是流量,流量突增引发系统开启大量线程进行处理,线程状态处于 Running 或 Runable 状态,这时操作系统(分时操作系统)会让 CPU 进行分时处理,从而引发 CPU 使用率上升。多线程并发处理会让服务器处理速度变快,但对于单个服务响应会变慢。
还有我们一般的系统架构部署多是 Nginx - Tomcat,而 Tomcat 默认线程数多是1000上限,且Tomcat 7以下版本默认是 BIO 模式,超过上限的线程会在 Tomcat 阻塞队列里等待,所以对于调用方来说,感知的时间会更久一些。
线程数为什么减少?
我们查看 JVM 的是配置内容,Young GC 是 PS Scavenge,Full GC 是 PS MarkSweep。
(本图引自网络)
并行(Parallel)指多条 GC 线程并行工作,但此时用户线程处于等待状态。并发(Concurrent)指用户线程与 GC 线程同时执行(不一定是并行的,可能会是交替执行);用户程序线程继续运行,而垃圾收集线程运行于另一个 CPU 上。
所以,Young GC 是 PS Scavenge,在 GC 时会出现 stop-the-world 的情况,此时用户线程处于阻塞情况,所以瞬时线程数会降低,当 GC 结束后,用户线程会恢复执行,所以又会上去。
为什么会发生这么多 Young GC?
Young GC 是对 Young Generation 的垃圾收集,YGCeden 空间不足会进行 Eden 和 Survivor 的 YGC。一个线程会在内存创建一个堆栈的空间,多线程会频繁的创建和释放内存空间,所以 YGCeden 会进行频繁的垃圾回收,因此会发生这么多 YGC。
我们知道 JVM 大多数采用主动式中断,即 GC 需要中断线程的时候,它就设个标志位(safe-point),执行线程会主动轮询这个标志位,如果标志位就绪的话就自行中断。
堆内存为什么减少了?
我们观察到堆内存的使用情况也降低了。
这是因为 PS MarkSweep 是标记-清除算法,PS Scavenge 是复制算法,GC 回收是进行内存无引用的内存回收,所以会让对内存占用减少。
思考
考虑现有系统的应用场景,如果是网关系统,频繁的 Young GC 和 Full GC 在 stop-the-world 发生时,一定会影响调用方请求,极端情况下出现 502 的问题。所以,在设计系统的时候,还是要尽量减少 Young GC 和 Full GC。