jdon导致cpu 99%最后tomcat死掉---banq给予回复

问题:最近发现客户的机器tomcat进程的cpu经常在99%基本上导致应用死掉
分析:通过分析,发现很多线程停留,查看堆栈信息如下:
http-80-1 RUNNABLE java.lang.Thread
java.util.HashMap.get(Unknown Source)
com.jdon.container.pico.JdonPicoContainer.getInstance(JdonPicoContainer.java:327)
com.jdon.container.pico.JdonPicoContainer.getComponentInstance(JdonPicoContainer.java:309)
org.picocontainer.defaults.BasicComponentParameter.resolveInstance(BasicComponentParameter.java:77)
org.picocontainer.defaults.ComponentParameter.resolveInstance(ComponentParameter.java:117)
org.picocontainer.defaults.ConstructorInjectionComponentAdapter.getConstructorArguments(ConstructorInjectionComponentAdapter.java:193)
org.picocontainer.defaults.ConstructorInjectionComponentAdapter$1.run(ConstructorInjectionComponentAdapter.java:148)
org.picocontainer.defaults.ThreadLocalCyclicDependencyGuard.observe(ThreadLocalCyclicDependencyGuard.java:56)
org.picocontainer.defaults.ConstructorInjectionComponentAdapter.getComponentInstance(ConstructorInjectionComponentAdapter.java:184)
com.jdon.container.pico.PicoContainerWrapper.getComponentNewInstance(PicoContainerWrapper.java:197)
com.jdon.bussinessproxy.target.POJOObjectFactory.create(POJOObjectFactory.java:45)
com.jdon.bussinessproxy.target.DefaultTargetServiceFactory.create(DefaultTargetServiceFactory.java:59)
com.jdon.aop.reflection.MethodInvokerUtil.createTargetObject(MethodInvokerUtil.java:102)
com.jdon.aop.reflection.ProxyMethodInvocation.methodInvoke(ProxyMethodInvocation.java:97)
com.jdon.aop.reflection.ProxyMethodInvocation.proceed(ProxyMethodInvocation.java:76)
com.jdon.aop.interceptor.SessionContextInterceptor.invoke(SessionContextInterceptor.java:76)
com.jdon.aop.reflection.ProxyMethodInvocation.proceed(ProxyMethodInvocation.java:84)
com.jdon.aop.interceptor.StatefulInterceptor.invoke(StatefulInterceptor.java:82)
com.jdon.aop.reflection.ProxyMethodInvocation.proceed(ProxyMethodInvocation.java:84)
com.jdon.aop.interceptor.PoolInterceptor.invoke(PoolInterceptor.java:91)
com.jdon.aop.reflection.ProxyMethodInvocation.proceed(ProxyMethodInvocation.java:84)
com.jdon.aop.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:69)
com.jdon.aop.reflection.ProxyMethodInvocation.proceed(ProxyMethodInvocation.java:84)
com.jdon.aop.AopClient.invoke(AopClient.java:95)
com.jdon.bussinessproxy.dyncproxy.DynamicProxyWeaving.invoke(DynamicProxyWeaving.java:62)
$Proxy11.getKh_khddById(Unknown Source)
cn.vetech.framework.pszx.model.Kh_khdd_jcqp_tosave_action.checkB2BFKF(Kh_khdd_jcqp_tosave_action.java:943)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
org.apache.struts.actions.DispatchAction.dispatchMethod(DispatchAction.java:274)
org.apache.struts.actions.DispatchAction.execute(DispatchAction.java:194)
org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:419)
org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:224)
org.apache.struts.action.ActionServlet.process(ActionServlet.java:1194)
org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:414)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
cn.vetech.framework.asms.AgentFilter.doFilter(AgentFilter.java:54)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
com.jdon.util.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:92)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:615)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:877)
org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:594)
org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1675)
java.lang.Thread.run(Unknown Source)

这个线程基本上卡住,然后越来越多,线程是卡住在java.util.HashMap.get(Unknown Source)
,然后我查看com.jdon.container.pico.JdonPicoContainer.getInstance(JdonPicoContainer.java:327)
这个源码发现里面有一个专门缓存pojo这个配置的实例对象的一个hashmap,代码如下:
public Object getInstance(ComponentAdapter componentAdapter)
{
Object componentKey = componentAdapter.getComponentKey();
Object instance = this.componentKeyToInstanceCache.get(componentKey);
if ((instance == null) &&
(componentAdapter != null)) {
instance = getTrueInstance(componentAdapter);
if (instance != null) {
this.componentKeyToInstanceCache.put(componentKey, instance);
}
}

return instance;
}
这个代码其中 Object instance = this.componentKeyToInstanceCache.get(componentKey);的定义是:
public class JdonPicoContainer
implements MutablePicoContainer, Serializable
{
public static final String module = JdonPicoContainer.class.getName();

private Map componentKeyToAdapterCache = new HashMap();

private Map componentKeyToInstanceCache = new HashMap();
..............

componentKeyToInstanceCache 这个里面存放我定义的service和dao,jdon框架在我获取一个service或者dao的时候判断在这个componentKeyToInstanceCache里面有没有实例,如果没有就实例化一个对象并缓存到这个map中,下次我再次使用这个对象的时候直接从缓存里面取,那么问题是这个componentKeyToInstanceCache不是一个线程同步的对象,因为JdonPicoContainer是一个单例。componentKeyToInstanceCache这个对象相当于是一个全局的。因此如果多线程并发的应用下,这个地方肯定会出现问题。而我为了找这个问题已经都快疯了。然后我下载最新的jdon的框架发现,这个类已经被修改成 private Map componentKeyToInstanceCache = new ConcurrentHashMap(),既然做了调整为什么在网站上没有说明并告知。导致我们使用者带来巨大的麻烦。

本人可能分析有误见谅,希望作者给与回复
[该贴被mianwo602于2012-06-01 13:47修改过]

2012-06-01 13:41 "@mianwo602"的内容
既然做了调整为什么在网站上没有说明并告知。导致我们使用者带来巨大的麻烦。 ...

非常抱歉,给你带来这么多麻烦。

这部分代码原来是PicoContainer 1.1版本的,我进行了重写,后来发现它还存在并发问题,又一次进行了重写,抱歉没有及时通知。

现在最新版本经过多次并发测试,已经基本不存在死锁问题,唯一要注意的是JVM 永生代占有量大小变化,JVM上进行优化。

[该贴被banq于2012-06-01 15:39修改过]

我现在没有更换高版本,只是把我那个版本的源码改了
改的内容是将hashmap换成了ConcurrentHashMap
banq老师您觉得这个还有问题吗?

关键要确认死锁在这里,用jstack将堆栈输出,寻找BLOCKING

我现在没有更换高版本,只是把我那个版本的源码改了
改的内容是将hashmap换成了ConcurrentHashMap
banq老师您觉得这个还有问题吗?

我发现死锁的地方有3个大概是:
JdonPicoContainer
HttpSessionProxyVisitor
SessionContext

我说的3个地方我确认是在这里,我已经跟踪了好长时间分析日志找出来的

需要显示BLOCKING,我看到是RUN,内存分配太小也会触发垃圾机制频繁启动。

这个问题是这样的
我为什么把run的线程打出来呢,因为那个hashmap的get方法有一个循环,我再网上查了的这个get方法会导致死循环,就是因为并发访问导致的。所以这个线程不是bloking 状态而是运行状态。

好吧,你再上线试验看看,希望这就是问题症结。如果还有问题,将JVM的gc日志定时输出,跟踪垃圾回收使用的时间。
[该贴被banq于2012-06-01 17:17修改过]

非常感谢您的建议和回答

大哥,那是死循环不是死锁,为啥要找BLOCKING? HASHMAP在多线程环境下使用会产生死循环,这是常识。

2012-06-13 13:27 "@attend"的内容
那是死循环不是死锁,为啥要找BLOCKING? HASHMAP在多线程环境下使用会产生死循环,这是常识。 ...

Jdon框架在第一次启动时,也就是PicoContainer会将HashMap中所有需要依赖的实例都注射完成,因此,以后在访问时,HashMap是只读,没有新的元素放入PUT,所以不存在HashMap死循环问题。

所以,Jdon框架启动后,使用模拟程序启动一下Jdon应用程序即可。

楼主这个问题,看上去不是启动时问题,而是使用一段时间有CPU超载,我估计问题不在Hasmap这里。
[该贴被banq于2012-06-13 13:45修改过]

我现在把hashmap改成同步后再没有出现那种问题了。
源头确实是hashmap,不过你的新版本也是这么处理的。肯定有这方面的原因

2012-06-28 15:57 "@mianwo602"的内容
源头确实是hashmap,不过你的新版本也是这么处理的。肯定有这方面的原因 ...

源头找到就好,并不是所有的依赖注入在pico启动时都注入完毕的,pico注入是使用时进行注入,因此有可能在运行过程中还会激活注射,所以对HashMap有写入操作。几年前的机制记忆有些模糊。

我当时是在复核代码时,发现这段从pico源码中移植过来的代码有线程问题,顺手就把它补上,所以,没有想到这里运行真出了问题,感谢楼主。

另外,因为Jdon框架集成缓存,JVM配置时,建议将新生代配小点,旧生代大一些,如2G内存,XMX使用1.7G,新生代使用200M即可,余下大概1.3G留给旧生代,对性能提升作用比较大。

[该贴被banq于2012-06-28 18:25修改过]