多图预警!java
从JVisualVM的监控视图中,咱们能够直观的看出每隔一分钟都会出现线程数飙升、类加载数阶梯式增加的状况。web
随着类加载数的增加,Metaspace空间逐步从60M增加到100M,出现内存溢出,致使jvm频繁触发full GC,消耗大量CPU资源。缓存
Task类 run方法代码:tomcat
红框部分为新增代码,具体实现以下:安全
主要逻辑是与底层组件通讯查询运行状态,而后根据结果更新状态。直接排除DAO操做的嫌疑,抽取与通讯部分,整理成单独的测试代码:服务器
步骤: 1. 设置jvm参数 : -verbose:class 打印类加载信息并发
2. 清理控制台日志,调试代码。jvm
调试过程当中,我发现每次循环都会有新的类被加载:工具
而这些类都是在下面这行代码运行以后加载的。测试
结合类加载信息以及sendRequest方法的实现,基本确认问题是由JaxbUtil处理xml、JavaBean的相互转换引发。
继续调试分析,发现JAXBContext对象初始化时会动态加载class,而JaxbUtil每次调用都会从新建立一个JAXBContext。
问题根因既已找到,解决思路天然清晰明确。
考虑到jdk中已有JAXB工具类提供xml和javaBean的互转,借鉴源码发现JAXB使用弱引用Cache对象来缓存JAXBContext。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
结合应用的实际场景,上面的实现避免了短期频繁建立JAXBContext。可是弱引用Cache在无引用的状况下会很快被GC回收,因此每次定时任务都会从新生成context;而且Cache对象只能存储一个context,在定时任务的运行过程当中可能因为其余接口通讯致使context切换。综上,JAXB的实现也没法知足当前应用的须要。
没有现成的解决方案,只好本身写一个。
由建立JAXBContext引发问题,那就延长对象的生命周期,减小新建对象。对于相同的Class,可使用同一个context对象与xml互相转换。因为vag的接口个数有限, 其xml报文格式并很少,所以,维护一个static Map<Class<?>, JAXBContext>来存储context对象占用的内存并很少。考虑到与vag通讯属于并发执行,使用ConcurrentHashMap实现保证并发安全。
最终代码以下:
将以前的测试代码模拟定时任务略微修改,每隔10s执行一次,重复50次。
开启JVisualVM监视视图,从图中能够明确的看出类装载数在第一次循环时就已接近最大值,后续过程当中只加载了极少数量的class,证实这种方案确实可行。
使用修改后的代码运行整个项目,16小时后的监视图像显示:类加载数保持稳定,MetaSpace大小几乎无变化。
另外,前段时间还使用-verbose:class 参数排查出Apache CXF生成的webservice客户端重复初始化引发的OOM问题的缘由。客户端初始化的过程当中也会根据wsdl文件动态生成class并加载,所以,使用CXF客户端代码时,应尽可能使用单例模式。