IO密集型应用特点
对于io密集型应用来说,比如文件上传、下载,存在处理耗时长,需要byte数组拷贝或者IO流的输入输出等,比较容易产生大对象并且存在堆内存时间较长,比较容易经过多次YGC后转移到老年代里面。
- 垃圾回收器选择
在生产中,我们首先选择了CMS作为垃圾回收器,我们知道CMS垃圾回收器新生代采用的是ParNew作为垃圾回收器,老年代采用了CMS作为垃圾回收器。CMS垃圾回收器作为并发垃圾收集器,采用的算法是标记清除算法。标记清除算法特点是收集时间短,低停顿,缺点是会产生内存碎片,对于需要连续空间的大对象来说,一旦空间不足,容易再次触发fullgc,在生产此刻表现为cpu飙升,内存占用高达90%以上,虚拟机监控出现一段时间空白。具体如下图所示:
生产此应用作为pdf文件生成、pdf文件转图片、图片上传等业务,接口响应时间长,Pdf处理采用pdfbox,比较消耗内存,生产机器配置为2核4G虚拟机。
回顾CMS垃圾回收算法,CMS垃圾收集器基于标记-清除算法实现的。总共分为:
- 初始标记
- 并发标记
- 重新标记
- 并发清除
初始标记需要停顿用户线程即STOP THE WORLD,初始标记从GC ROOTS出发标记能关联到的所有对象,速度非常快。并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对
象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与用户线程一起并发运行。而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短;最后是并发清除阶段,清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
由于允许用户线程同时运行,会造成在GC过程中不断的有对象进入内存,当前GC没有完成,空间没有释放,可能导致没有足够的空间放新进来的对象,此时会导致 STW,然后退化成 serial 收集器进行垃圾回收。
为了解决此问题,需要对虚拟机进行优化,我们更换了垃圾回收器,改成虚拟机默认垃圾回收器,也就是新生代是Parallel Scavenge收集器,老年代采用个PS MarkSweep收集器也即Serial Old,优化上线后,没有再产生以上问题。
我们知道serial old收集器采用标记整理算法,标记-清除算法与标记-整理算法的本质差异在于前者是一种非移动式的回收算法,而后者是移动式的。
对于大对象来说,当老年代内存不够分配时,会整理出连续的空间供大对象进行配置,故不会出现连续进行fullgc导致cpu飙升情况。