服务性能优化,发现有个服务的ygc次数和单次ygc耗时明显比同类型服务高。
通过jstat(jstat -gcutil pid 100 1000)发现,每次ygc之后,surivor中都有20-30%被占用,而同类型服务ygc之后,surivor只有3-5%的被占用,说明eden中有不少的对象熬过了本次ygc,每次ygc都有几百兆的对象能挺进surivor,确实有点可疑。
只好dump下机器内存,看下能否发现明显的异常之处,看了dump内存吓了一跳,一半左右的对象都来自java.lang.ref.Finalizer
内存中的对象 Finalizer中对象信息而Finalizer对象中持有的都是FileInputStream和FileOutputStream。这两个类都是重写了Object的finalize()方式的,看代码,重写只是为了确认释放资源(gc只能回收jvm中的资源,对于jvm之外的无能为力,FileInputStream需要通过jni获取系统资源,这部分资源需要显式回收)。
FileInputStream的finalize()方法创建覆写了finalize方法的类对象(O1)时,同时会创建一个Finalizer对象(F1),F1会引用O1。在发生GC时,O1不会被直接回收,而是会被JVM识别出,JVM会将F1放入Finalizer的static的Queue中。所以,一次ygc并不能回收O1,只能转移到survivor。
那O1什么时候才能被回收呢?原来JVM在加载Finalizer对象的时候,会启动一个名为“Finalizer”的线程,负责清理Finalizer的Queue中的对象。清理的过程,就是执行O1的finalize方法,并同时从队列中移除。被清理之后,再没有引用指向实现了finalize方法对象了,那么在下一次GC的时候,此对象就可以被回收了 。
原因查到了,至于怎么修复
1、升级jdk,高版本jdk中的FileInputStream类已经删除了finalize()方法
2、避免使用覆写了finalize()的类
网友评论