网站持久代引发Full GC问题分析

现状:

Dragoon(监控系统)的日报显示trade_us_wholelsale(美国wholesale集群),日均Young GC次数25w次左右,应用暂停295w毫秒(相当于40多分钟),Full GC次数600次左右,应用暂停190w毫秒(相当于30多分钟)。

GC,尤其是Full GC,每次都会导致JVM暂停工作,处理垃圾回收任务,短时间内无法响应用户请求,大量的Full GC会导致系统响应速度降低,而且引来OOM的巨大风险。

分析:

启动参数:

trade_us_wholesale应用的JVM启动参数如下:

-Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k-XX:+UseConcMarkSweepGC

堆内存=新生代+老年代:-Xmx2g -Xms2g

新生代:256M

老年代:1792M(2G-256M)

持久代:-XX:PermSize=128M

栈内存:-Xss256KB

新生代回收方式:并行回收

老年代回收方式:并发回收(CMS)

实时数据分析:

数据来源于Dragoon平台

l  分代内存占比

JVM分代 使用内存量 内存总量
持久代 117M-125M 128M
新生代 220M-225M 256M
旧生代 800M-1G 1792M

注:明显看出,新生代以及持久代,内存使用较为紧张,旧生代较为宽裕。

l  Young GC

Young GC频繁,由于并行GC采用多线程回收方式,但是考虑到GC次数很高,由于GC每次都会暂停应用,因此对应用的影响是不可忽视的。

l  Full GC

Full GC是指新生代、旧生代、持久代一起进行GC。

引起Full GC的原因主要有三种:

  1. 旧生代内存不足
  2. 持久代内存不足
  3. 统计新生代 GC晋升到旧生代的平均大小大于旧生代的剩余空间

Full GC回收成本高,应用会因此暂停更长时间,无法响应用户的请求。

l  Class Load情况

绿线表示卸载类的数量的累加总数,红线表示总共装载过的类的数量的累加总数,蓝线表示当前装载的类的数量的累加总数。绿线和红线的发展趋势是极其一 致的,即有大量的类被加载之后,又被卸载。加载类的信息存放在持久代。这也就从某方面印证了,持久代可能是在反复的加载类和卸载类,什么原因造成这种现 象,GC的可能性较大,我们接着看后续的分析。

GC Log情况

我们截取线上GC log的中的一段进行分析:

2011-08-23T04:24:27.413-0700: 611.162: [Full GC 611.162: [CMS: 985319K->721699K(1835008K), 2.9525910 secs] 1152585K->721699K(2070976K), [CMS Perm : 131071K->103472K(131072K)], 2.9528010 secs] [Times: user=2.87 sys=0.08, real=2.96 secs]

l  解释:

持久代:131071K->103472K 总空间:131072K

旧生代:985319K->721699K 总空间:1835008K

堆内存:1152585K->721699K 总空间:2070976K

此次Full GC时间花费2.96秒,在用户态(user)占用CPU百分比2.87,系统态(sys)占用百分比0.08。

l  分析:

数据很清晰了,持久代的GC是导致Full GC的原因,这和我们之前看到的类信息的加载之后又卸载之后又加载的现象是相符的。持久代空间不够,引起GC,GC回收类的信息,但是由于回收的类信息 JVM之后还需要,因此JVM又去加载这些信息。JVM就在这反复加载和卸载中消耗着资源,无暇响应用户的请求。

Unloading Log情况

我们继续观察显示控制台打出的unload的日志,这里打印出卸载类的情况。

sun.reflect.GeneratedSerializationConstructorAccessor这是sun的反射机使用到的类,当 事业反射newInstance()时,JVM会根据-Dsun.reflect.noInflation和 -Dsun.reflect.inflationThreshold判断是使用本地代码的反射构造器还是使用动态生成字节码构造出来的构造器,默认配置是 将会使用后者,这种方式的构造上更慢,但运行速度更快,对于使用大量反射的框架来言,后者更加合适。 GeneratedSerializationConstructorAccessor就是动态生成字节码构造出来的,后面的数字是表示累计创建类的数 量。针对一个类,同一个类加载构造器,反射的ConstructorAccessor只有一个。

可是wholesale短短启动不到3小时,怎么会有这么多ConstructorAccessor产生而且还被卸载呢?这些类是为了反射什么类而创建的呢?继续分析,下面只能dump jvm,看看内存中的情况具体是怎样了?

还有一点,线上的预发布机没有这种现象,没有大量的unloading日志,没有Full GC,他们的硬件和软件都是一样的,唯一不同的就是PV,这点很重要。

Dump JVM

我们去线上dump下来jvm的信息,使用MAT打开,看classloader Explorer,发现更多信息。

我们看到大量的DelegatingClassLoader,而且看到了GeneratedSerializationConstructorAccessor,

我们查看都有哪些类被加载,很明显JVM当中有大量的GeneratedSerializationConstructorAccessor+count类。

这说明JVM当中有很多ConstructorAccessor一大部分被卸载了,一小部分被留在JVM当中。而且,大家注意每个 ConstructorAccessor类都有自己独立的classloader,类信息的卸载规则是按照classloader来卸载,只有这个 loader中加载的所有类都不被使用,这些类信息才能被卸载。线上的信息好像就这么多了,我们看看DelegatingClassLoader能不能在 测试环境中debug到,看看具体是什么问题。

测试环境日志

我们在测试环境搭建一套wholesale环境,使用Jmeter小幅度压测wholesale的detail页面,随着wholesale稳 定,load日志出现明显的规律性现象。每次访问detail页面,都会有 GeneratedSerializationConstructorAccessor类

上图就是在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分钟左右),大大缩短了集群的暂停时间,基本解决问题。

 

Full GC有关问题学习分析(转载)的更多相关文章

  1. JAVA GC垃圾收集器的分析

    本篇文章主要介绍了"JAVA GC垃圾收集器的分析",主要涉及到JAVA GC垃圾收集器的分析方面的内容,对于JAVA GC垃圾收集器的分析感兴趣的同学可以参考一下.       ...

  2. NAACL 2019 字词表示学习分析

    NAACL 2019 表示学习分析 为要找出字.词.文档等实体表示学习相关的文章. word embedding 搜索关键词 word embedding Vector of Locally-Aggr ...

  3. JVM学习(4)——全面总结Java的GC算法和回收机制---转载自http://www.cnblogs.com/kubixuesheng/p/5208647.html

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 一些JVM的跟踪参数的设置 Java堆的分配参数 -Xmx 和 –Xms 应该保持一个什么关系,可以让系统的 ...

  4. EasyUI学习总结(三)——easyloader源码分析(转载)

    声明:这一篇文章是转载过来的,转载地址忘记了,原作者如果看到了,希望能够告知一声,我好加上去! easyloader模块是用来加载jquery easyui的js和css文件的,而且它可以分析模块的依 ...

  5. R语言学习 - 非参数法生存分析--转载

    生存分析指根据试验或调查得到的数据对生物或人的生存时间进行分析和推断,研究生存时间和结局与众多影响因素间关系及其程度大小的方法,也称生存率分析或存活率分析.常用于肿瘤等疾病的标志物筛选.疗效及预后的考 ...

  6. paper 53 :深度学习(转载)

    转载来源:http://blog.csdn.net/fengbingchun/article/details/50087005 这篇文章主要是为了对深度学习(DeepLearning)有个初步了解,算 ...

  7. Hadoop家族学习路线图--转载

    原文地址:http://blog.fens.me/hadoop-family-roadmap/ Sep 6, 2013 Tags: Hadoophadoop familyroadmap Comment ...

  8. JVM知识整理和学习(转载并修改)

    JVM是虚拟机,也是一种规范,他遵循着冯·诺依曼体系结构的设计原理. 冯·诺依曼体系结构中,指出计算机处理的数据和指令都是二进制数,采用存储程序方式不加区分的存储在同一个存储器里,并且顺序执行,指令由 ...

  9. Netty系列之Netty可靠性分析--转载

    原文地址:http://www.infoq.com/cn/articles/netty-reliability 1. 背景 1.1. 宕机的代价 1.1.1. 电信行业 毕马威国际(KPMG Inte ...

随机推荐

  1. postgresql 函数 参数为复合类型

    postgresql没有存储过程,但是函数功能很强大. 在近期开发的电商管理平台中,对于产品的类目管理,设计时有个属性字段,设为字符数组,但是EF不支持数组的操作,所以在添加和修改类目时,需要对属性的 ...

  2. 误删system04.dbf 报:ORA-01157 ORA-01110

    DB:Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production OS:Oracle Linux 5.7 ...

  3. oracle11g 重新配置em

    OS: ORACLE-LINUX 5.7 DB: 11.2.0.3 [oracle@b28-122 ~]$ emctl status dbconsoleOracle Enterprise Manage ...

  4. qtp 设置等待时间

    1.file->settings->run .默认的时间是20 秒 2. browser("browser").Navigate http://www.baidu.co ...

  5. poj 2046 Gap

    题目连接 http://poj.org/problem?id=2046 Gap Description Let's play a card game called Gap. You have 28 c ...

  6. core java 5~6(OOP & 高级语言特征)

    MODULE 5 OOP 面向对象程序设计--------------------------------------------------------Object Oriented Program ...

  7. Java之有病的policy配置

    使用-Djava.security.policy=xxx.policy启动安全策略, 你会想到codesource的配置如此蛋疼么? grant CodeBase "file:////D:/ ...

  8. union与union all的区别

    首先说下union与join的区别 1.union是以行增加的方式,进行连接:join是以列增加的方式进行连接: 2.union连接查询的两个表的字段必须要一一对应,数目相等:join则没有要求,但是 ...

  9. Google Guava学习笔记——基础工具类String处理类的使用

    不管你喜欢何种编程语言,很多时候针对string编程的处理都是乏味而且爱出错误的,很多时候,我们需要从文件或是数据库中读取数据,或者根据需求重新格式化或排序字符串给用户显示.幸运的是,Guava提供了 ...

  10. shell 进程查询相关的命令

    同一个进程部署多个,如何知道删除哪个: ll /proc/ 会显示位置 查看pid下哪些socket链接 ll /proc//fd