产品issue 诊断:Java CPU/Memory- Caused by JAX-WS Client
2011-11-15 18:14
501 查看
Background
我们有个组件使用JAX-WS client来call另外一个组件的web service。新的release发布后,立马出现了cpu issue。Cpu issue
截图如下(来自Cacti)经调查发现,是因为我们的Stub对象创建过于频繁所致。每次API调用,都会新建一个Stub对象(创建代码如下)。而JAX-WS Stub对象的创建是cpu-sensitive的过程
static String SERVICE_URL = ...; static String NAMESPACE_URI = ...; public static MyService getMyService() { String endpoint = "http://" + SERVICE_URL + "/ws/MyService.wsdl"; URL url = new URL(endpoint); QName qname = new QName(NAMESPACE_URI, "MyServiceService"); MyServiceService service = new MyServiceService (url, qname); MyService port = service.getPort(MyService.class); BindingProvider bp = (BindingProvider) port ; bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint); return port; }
很显然,我们必须重用stub对象,而不能每次重新创建。但问题是:
Is JAX-WS client thread-safe
如果你有google过这个问题,你一定很恼火Sun的JAX-WX RI的开发人员。很多人希望有个官方回答,但只有非官方的线索。Stackoverflow,CXF,JBOSS
“根据JAX-WS的Spec, client proxy 是非线程安全”
经过考虑后,我们有几种方案:
创建一个全局的Stub。我们有程序员认为(未验证)Stub中全局共享的context变量在创建后并没有被修改,其他的都是每次调用生成的局部对象。所以单个Stub是线程安全的。
使用ThreadLocal,每个线程cache一个Stub对象
使用ObjectPool,类似于ThreadLocal,但不委托于Tomcat的Thread pool来管理对象的生命周期,而是自己来申请/释放
不使用JAX-WS,使用在其他项目中得到检验的Axis2的client包
从代码change的范围来讲,1~4排列,从小到大。1是将自己放在推测上,舍弃;于是用2
修改上线后,CPU下降显明,我们过了几天好日子。
左图为每次重新创建Stub,右图为重用Stub对象。(来自JConsole)
Memory Issue
截图如下(来自jvisualvm),使用的内存缓慢上升;运行几天后,内存被耗尽(2G),开始频繁的GC,导致CPU再次相当繁忙。通过对Heap-Dump(来自MAT)的分析,我们发现占用Heap的对象为线程对象。
java.lang.Thread @ 0x75426748 http-8930-1 - 4,353,584 (1.02%)bytes.
Thread中retained对象是ThreadLocal,而ThreadLocal中cache了Stub对象, 每个Stub对象为1M左右。由于Tomcat默认线程池大小是200个,
我们的应用中每个线程会cache三个Stub对象,所以理论上Stub占用的内存为600M。
注意在我们的Tomcat中部署了多个应用,这些应用共享Tomcat的线程池,所以线程空闲的机会很少,所以线程被销毁的机会也就恨小。
在产品环境,我们发现内存占用可达到1.8G,与理论里不符。
在检查了该组件的内存使用历史记录后,我们发现这个组件在使用JAX-WS client之前内存使用也一直很高。
JAX-WS Client成了压垮它的最后一根稻草。
关于ThreadLocal + ThreadPool的组合, 网上有文说it's a bad idea,至少要慎用
Second Fix
在Memory issue出现后,我们又重新从头到审查这个issue。我们发现在stub的创建过程,耗时操作在Service的创建上,而Port(Stub)的创建相对要轻量很多,于是我们作了如下的Fix:
全局共享一个Service对象,Port对象每次API调用重新生成。
static String SERVICE_URL = ...; static String NAMESPACE_URI = ...; static MyServiceService service = null; static { String endpoint = "http://" + SERVICE_URL + "/ws/MyService.wsdl"; URL url = new URL(endpoint); QName qname = new QName(NAMESPACE_URI, "MyServiceService"); MyServiceService service = new MyServiceService (url, qname); } public static MyService getMyService() { MyService port = service.getPort(MyService.class); BindingProvider bp = (BindingProvider) port ; bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint); return port; }
Still issue
产品上最终CPU/Memory没有问题。但关于JAX-WS client stub(port)是否为线程安全,目前还没给出Final conclusion.
有人说,it's
thread-safe
相关文章推荐
- Caused by: java.lang.OutOfMemoryError: Java heap space
- MyEclipse 9 出现"Caused by: java.lang.OutOfMemoryError: PermGen space"解决
- Caused by: java.lang.RuntimeException: by java.lang.OutOfMemoryError: PermGen space(tomcat 启动时提示内存溢出)
- Tomcat启动项目Caused by: java.lang.OutOfMemoryError: Java heap space
- spark Caused by: java.lang.OutOfMemoryError: Java heap space 问题
- Tomcat:Caused by: java.lang.OutOfMemoryError: PermGen space解决方案
- Tomcat:Caused by: java.lang.OutOfMemoryError: PermGen space的解决方案
- Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
- Caused by: java.lang.OutOfMemoryError: PermGen space异常处理
- Android中 加载一张大图片Caused by: java.lang.OutOfMemoryError
- MyEclipse 8.6 出现"Caused by: java.lang.OutOfMemoryError: PermGen space"解决
- Eclipse Tomcat 内存不足:Caused by: java.lang.OutOfMemoryError: Java heap space
- Caused by: java.lang.OutOfMemoryError: PermGen space
- Caused by: java.lang.OutOfMemoryError: Java heap space 异常
- Tomcat:Caused by: java.lang.OutOfMemoryError: PermGen space
- Caused by: java.lang.OutOfMemoryError: Java heap space解决办法
- Tomcat:Caused by: java.lang.OutOfMemoryError: PermGen space
- Tomcat:Caused by: java.lang.OutOfMemoryError: PermGen space
- Tomcat:Caused by: java.lang.OutOfMemoryError: PermGen space的解决方案
- Tomcat:Caused by: java.lang.OutOfMemoryError: PermGen space