网站持久代引发Full GC问题分析
JVM分代使用内存量内存总量持久代117M-125M128M新生代220M-225M256M旧生代800M-1G1792M
注:明显看出,新生代以及持久代,内存使用较为紧张,旧生代较为宽裕。
l? Young GC
上图就是在10个线程并发访问wholesaledetail页面,load日志中的内容,问题应该是出现在这些类的加载上。我们debug一下,看看具体情况。
我们debug Classloader这个类的构造器,一次访问是有哪些classloader构造出来,最终debug到 DelegatingClassLoader,结合load日志,发现这个classloader加载后就会有 GeneratedSerializationConstructorAccessor被加载。依据堆栈查看调用信息。
我们看到ReflectionConverter的中的一段代码:
final Object result = instantiateNewInstance(context, reader.getAttribute(mapper.attributeForReadResolveField()));
ReflectionConverter是XStream的反射转换器,转换器是用来编码或解码Java对象的,也就是负责java对象和xml直 接的转换。instantiateNewInstance使用来初始化类的实例的,其中 reader.getAttribute(mapper.attributeForReadResolveField())就是 WholesaleProductPriceXstreamDTO类。
instantiateNewInstance方法中会先去context当中取这个类的实例,如果没有将会使用反射构造出类的实例,在构造实例时,他们创建新的类加载器,并执行下面的一段代码:
Constructor customConstructor = reflectionFactory.newConstructorForSerialization(type, javaLangObjectConstructor);
这段代码就是动态字节码创建类构造器的代码,我们debug发现customConstructor对象中正好包含sun.reflect.GeneratedSerializationConstructorAccessor对象。
而这段代码是在我们的业务二方库biz.wsproduct中调用的:
XStream xstream = new XStream();
xstream.alias(“root”, List.class);
xstream.alias(“record”, WholesaleProductPriceXstreamDTO.class);
问题很清楚了,就是这个XStream,业务代码中错误的new这个类,造成每次执行到这段代码都需要执行上面讲述的一段逻辑,都会load GeneratedSerializationConstructorAccessor类。
而且众所周知,wholesale detail pv巨大(200w以上),大量的类被加载,应该是造成PermGen使用吃紧的原因,而且由于是创造一个新的类加载器,这样一来一次访问结束,就可以回 收这个类加载器,回收其中的类信息,释放部分PermGen空间。这样我们就看到大量的unload出现,而且PermGen虽然空间紧张,但是从未 OOM的问题也就很容易解释了。
整体的堆栈信息如下(部分堆栈信息省略):
Daemon Thread [TP-Processor39] (Suspended)
DelegatingClassLoader(ClassLoader).<init>(ClassLoader) line: 207
DelegatingClassLoader.<init>(ClassLoader) line: 54
…
MethodAccessorGenerator.generate(Class, String, Class[], Class, Class[], int, boolean, boolean, Class) line: 377
MethodAccessorGenerator.generateSerializationConstructor(Class, Class[], Class[], int, Class) line: 95
ReflectionFactory.newConstructorForSerialization(Class, Constructor) line: 313
Sun14ReflectionProvider.getMungedConstructor(Class) line: 61
Sun14ReflectionProvider.newInstance(Class) line: 40
ReflectionConverter.instantiateNewInstance(UnmarshallingContext, String) line: 145
ReflectionConverter.unmarshal(HierarchicalStreamReader, UnmarshallingContext) line: 87
…
XStream.unmarshal(HierarchicalStreamReader, Object, DataHolder) line: 521
XStream.unmarshal(HierarchicalStreamReader, Object) line: 509
XStream.fromXML(Reader) line: 475
XStream.fromXML(String) line: 468
WholesaleProductPriceXstreamHelper.convertXmlToWholesaleProductPriceDtoList(String) line: 55
WholesaleDetailUtil.getPriceDetailDTOFormProduct(WsProductDO, String) line: 404
WholesaleDetailUtil.getProductInfoDTOFromProduct(WsProductDO, String) line: 158
WholesaleProductDetail.process(RunData, TemplateContext) line: 282
WholesaleProductDetail(BaseDetailScreen).execute(RunData, TemplateContext) line: 73
WholesaleProductDetail(TemplateModule).execute(RunData) line: 38
…
解决方案:解决方案比较简单,我们将XStream的设置成static变量,保证JVM当中只有一个这样的实例,在static块当中初始化,代码如下:
??? private static XStream xstream = null;
??? static {
??????? xstream = new XStream();
??????? xstream.alias(“root”, List.class);
?xstream.alias(“record”, WholesaleProductPriceXstreamDTO.class);
??? }
?
解决效果:根据最新的Dragoon监控日报显示:
Full GC的次数由原先的647次降至157次,
应用暂停时间由原来的1943232.0 ms (30分钟左右)降至488444.0 ms (8分钟左右),大大缩短了集群的暂停时间,基本解决问题。