- 浏览: 581639 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (188)
- java (14)
- web (14)
- web service (3)
- 杂谈 (14)
- Version Control (13)
- software test (30)
- linux (17)
- database (3)
- distributed storage and computing (1)
- ejb (7)
- project building (46)
- spring & IOC (2)
- Thread (2)
- xml (2)
- tool software (0)
- [网站分类]1.网站首页原创Java技术区(对首页文章的要求: 原创、高质量、经过认真思考并精心写作。BlogJava管理团队会对首页的文章进行管理。) (0)
- project manager (9)
- OSGI (1)
- nosql (3)
最新评论
-
sp42:
好搞笑
你懂不懂xml! (2) -
cherishmmo2004:
感觉你们都很牛掰,我们做的一个运维平台也是用karaf的,用k ...
基于osgi开发大型的企业应用 -
liubey:
“自作聪明”的使用了读写锁,其实只使用ReentrantLoc ...
编码最佳实践(4)--小心LinkedHashMap的get()方法 -
liubey:
你这个代码是sublist后仍然一直持有这个sub的引用,一般 ...
编码最佳实践(5)--小心!这只是冰山一角 -
xiegqooo:
初学maven(5)-使用assembly plugin实现自定义打包
刚刚鄙视完sun,继续performance tuning,结果又发现问题:测试中,偶尔会出现一些古怪错误,经检查,发现有以下可疑的异常:
com.sun.xml.ws.client.ClientTransportException: HTTP transport error: java.util.ConcurrentModificationException
at com.sun.xml.ws.transport.http.client.HttpClientTransport.getOutput(HttpClientTransport.java: 134 )
at com.sun.xml.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java: 143 )
at com.sun.xml.ws.transport.http.client.HttpTransportPipe.processRequest(HttpTransportPipe.java: 89 )
at com.sun.xml.ws.transport.DeferredTransportPipe.processRequest(DeferredTransportPipe.java: 91 )
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java: 595 )
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java: 554 )
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java: 539 )
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java: 436 )
at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java: 106 )
at com.sun.xml.ws.tx.client.TxClientPipe.process(TxClientPipe.java: 177 )
at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java: 115 )
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java: 595 )
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java: 554 )
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java: 539 )
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java: 436 )
at com.sun.xml.ws.client.Stub.process(Stub.java: 248 )
at com.sun.xml.ws.client.dispatch.DispatchImpl.doInvoke(DispatchImpl.java: 180 )
at com.sun.xml.ws.client.dispatch.DispatchImpl.invoke(DispatchImpl.java: 206 )
at com.sun.jbi.httpsoapbc.OutboundMessageProcessor.outboundCall(OutboundMessageProcessor.java: 1256 )
at com.sun.jbi.httpsoapbc.OutboundMessageProcessor.dispatch(OutboundMessageProcessor.java: 1296 )
at com.sun.jbi.httpsoapbc.OutboundMessageProcessor.processRequestReplyOutbound(OutboundMessageProcessor.java: 747 )
at com.sun.jbi.httpsoapbc.OutboundMessageProcessor.processMessage(OutboundMessageProcessor.java: 257 )
at com.sun.jbi.httpsoapbc.OutboundAction.run(OutboundAction.java: 63 )
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java: 886 )
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java: 908 )
at java.lang.Thread.run(Thread.java: 619 )
Caused by: java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java: 793 )
at java.util.HashMap$EntryIterator.next(HashMap.java: 834 )
at java.util.HashMap$EntryIterator.next(HashMap.java: 832 )
at com.sun.xml.ws.transport.http.client.HttpClientTransport.createHttpConnection(HttpClientTransport.java: 364 )
at com.sun.xml.ws.transport.http.client.HttpClientTransport.getOutput(HttpClientTransport.java: 118 )
... 25 more
从异常上看,调用metro进行webservice的调用过程中,有对hashmap做游历进行读取的操作,期间抛出ConcurrentModificationException。感觉这个又是bug了,毕竟这个前前后后的代码,都是sun自己的:openESB, metro, jdk。
找到hashmap抛出异常的代码:
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
明显这个ConcurrentModificationException是hashmap主动抛出的,看条件if (modCount != expectedModCount)
找到expectedModCount,HashIterator构造时初始化为当时hashmap实例的modCount并保持不再修改,实际就是记下迭代开始时hashmap的状态:
expectedModCount = modCount;
...
}
再看modCount相关代码
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the HashMap fail-fast. (See ConcurrentModificationException).
*/
transient volatile int modCount;
从javadoc中可以得知modCount是用来记录hashmap实例的结构被修改的次数。同时明确指出这个域用来在做迭代时实现fail-fast。
现在非常明确的可以知道问题的来源了:在hashmap实例的坐迭代的过程中,其他线程修改了这个hashmap,导致modCount 和 expectedModCount不符,因此直接抛出ConcurrentModificationException)来实现fail-fast。
hashmap的这个做法没有问题,那么问题就是出在它的使用者上了:为什么在hashmap进行迭代的过程中,会修改这个hashmap?而且,明显的没有做同步保护,要知道hashmap是明确申明不是线程安全的。
先找到这个hashmap被调用的代码,metro下的HttpTransportPipe,方便起见请打开以下URL使用fisheye工具查看代码:
http://fisheye5.cenqua.com/browse/jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/transport/http/client/HttpTransportPipe.java?r=1.14
HttpClientTransport con;
try {
// get transport headers from message
Map < String, List < String >> reqHeaders = (Map < String, List < String >> ) request.invocationProperties.get(MessageContext.HTTP_REQUEST_HEADERS);
// assign empty map if its null
if (reqHeaders == null ){
reqHeaders = new HashMap < String, List < String >> ();
}
......
for (Map.Entry < String, List < String >> entry : reqHeaders.entrySet()) {
httpConnection.addRequestProperty(entry.getKey(), entry.getValue().get( 0 ));
}
出现问题的reqHeaders是从request中获取到的,明显是这个方法之外还有其他线程在修改这个hashmap。
简单修改一下代码:
HttpClientTransport con;
try {
// get transport headers from message
Map < String, List < String >> reqHeaders = new HashMap < String, List < String >> ();
Map < String, List < String >> reqHeadersInRequest = (Map < String, List < String >> ) request.invocationProperties.get(MessageContext.HTTP_REQUEST_HEADERS);
// assign empty map if its null
if (reqHeadersInRequest != null ){
reqHeaders.putAll(reqHeadersInRequest);
}
不直接使用原有的hashmap实例了,既然其他线程会同时进行修改操作,那么这个实例就是很不安全的了。我们重新new了一个新的HashMap,然后将原有HashMap的数据用putAll方法设置进入。用编译后的class文件覆盖glassfish/lib/webservice-rt.jar中的同名文件,重新测试,跑了20分钟,上述的ConcurrentModificationException异常没有再出现。
总结一下这个bug反映的问题,sun的开发人员在metro中是这样使用hashmap:
1. 将hashmap按照引用在各个实例间传递
2. 在不同地方有不同线程同时读写
3. 读写时不加锁,不做同步保护
很无语,这种做法,不是自己找死吗?hashmap不是线程安全,使用hashmap时并按照引用传递时,要不保证只读,要不就保证同时只有一个线程进行读写,前两者都不能保证就必须自己加锁做同步保护。
后来找到这个类的最新版本,发现在后面的版本中已经fix这个问题,有兴趣的话可以打开下面的URL看版本对比,sun官方的fix方式和我上面的完全一致,呵呵。
还有一个关联的issue,https://jax-ws.dev.java.net/issues/show_bug.cgi?id=467,看了一下内容,和我们的场景完全不一样,看来修改这个地方纯属巧合。
说点题外话,算是牢骚吧:
有点怀疑metro是不是根本就没有做过性能测试,我们的测试场景,openESB下通过bepl调用4个我们称为common service的webservice,目前大概做到1200个tps,算下来common service的webservice的tps大概是1200*4 = 5K附近,上面的问题就非常明显,之前tps没有上去前没有这么严重。
可以参考我之前的一个blog, http://www.blogjava.net/aoxj/archive/2010/04/29/319706.html,在解决这里提到的http long connection 和 TIME_AIT的问题之前,我们的tps比较低,cpu压不上去,当时好像这个问题不明显。后来搞定之后tps上来了才暴露出来。
考虑上一个blog中 == 比较无效导致cache失效的bug,我对metro的代码质量真是很没有信心。按说这样的大型项目,release之前怎么也要做做压力测试,稳定性测试之类,很容易发现类似问题才是。我相信,不是每个用metro的地方,tps都只需要跑几十tps而已吧。我在我的普通开发机上做测试,大概只能跑到100个tps,没有发现出错。换到比较强劲的机器,tps上到1000后,上面的错误立即凸现。
评论
不过同步的这个包是后来发布的,可以理解。
3篇文章我都看了,很佩服楼主的钻研精神。刚才没赶上点精华,这里补一个吧。
谢谢,谢谢!
putAll只是将对象引用复制一份作为新的hashmap的key/value,仅仅增加了一个hashmap + 一堆key/value引用,内存应该不是问题,不过cpu消耗会多一点。
修改之后的代码,我们昨天晚上跑了6个小时,没有再出现上面的异常,问题应该是解决了。
不过同步的这个包是后来发布的,可以理解。
3篇文章我都看了,很佩服楼主的钻研精神。刚才没赶上点精华,这里补一个吧。
发表评论
-
使用javap命令查看编译版本信息
2013-02-17 15:41 1548之前遇到几次现场故障,都是和class文件有关,比如版 ... -
编码最佳实践(6)--那些年,我们一起建的索引
2013-01-04 12:01 1421前几次的编码最佳实践系列,我们都着眼于Java代码,今 ... -
编码最佳实践(5)--小心!这只是冰山一角
2012-09-06 15:06 3226本期的案例依然是来自实际项目,很寻常的代码,却意外遭遇 ... -
解决junit4.4和jmockit的冲突
2011-04-25 17:26 4242今天开发中遇到的问题,代码提交到svn上去之后,hun ... -
javaOne归来后对java未来的看法和担忧
2010-12-20 17:50 2816上周参加北京的javaOne,今天回到办公室,刚刚在m ... -
[share]设计模式快速参考
2010-03-25 15:46 1454从网上找到的一个设计模式快速参考,感觉做的非常的好,分享给大家 ... -
MANIFEST.MF 文件中奇怪的分行和空格要求
2009-08-31 18:37 2254工作中发现的一个非常 ... -
jdk小工具jps介绍
2007-12-29 11:33 1152jps(Java Virtual Machine Proces ... -
在eclipse 3.4 Ganymede 中安装subclipse和subversive
2008-08-30 19:16 1549首先说一下这几天折腾eclipse 3.4和subclipse ... -
解决eclipse3.4版本在线安装插件超级慢的方法
2009-03-10 23:26 2339家里机器上用的eclipse最近莫名其妙的总是有问题,因此考虑 ... -
spring和依赖注入的价值
2008-01-11 22:26 4431看到有帖子,置疑spring和依赖注入的价值,回复内容整理如下 ... -
jdk小工具jps介绍
2007-12-29 22:34 10015jdk小工具jps介绍 jps(Java Virtual Ma ... -
java编译器对string常量表达式的处理和优化
2007-01-18 14:58 10116首先把问题摆出来,先 ...
相关推荐
### sun程序员认证参考知识点解析 #### 一、课程目标与内容概览 - **主要目标**:为学习者提供必要的知识与技能,以便能够有效地进行Java应用程序和Applets的面向对象编程。 - **核心内容**:涵盖Java编程语言的...
### SUN JAVA程序员模拟题知识点解析 #### 题目1: JDK组成部分 - **知识点**:JDK(Java Development Kit)组成 - **解析**:JDK主要由以下几个部分组成: - **Java编程语言**:Java编程语言是用于编写Java应用...
Sun认证Java程序员考试,全称为SCJP(Sun Certified Java Programmer),是Oracle公司(原Sun Microsystems)推出的针对Java编程技能的一项专业认证。这个考试主要面向希望验证自己Java编程基础的开发者,旨在评估...
Sun Java 认证考试题库 本题库涵盖了 Java 语言的基础知识和高级知识,包括 Java 入门、数据类型和运算符、流程控制与数组、封装、继承、抽象类与接口、多态、异常、多线程机制、输入输出流、泛型和集合框架、基于 ...
SUN公司(后被Oracle收购)推出的Java程序员认证课程是全球范围内Java开发者广泛认可的专业资格认证,旨在验证个人对Java语言的深入理解和应用能力。本课程主要针对Java 5.0版本,包括SCJP(Sun Certified ...
### Sun Java程序员模拟题知识点解析 #### 题目1: JDK组成部分 - **知识点**:JDK(Java Development Kit)组成 - **解析**:JDK主要由以下几个部分组成: - **Java编程语言**:这是开发Java应用程序的基础。 - *...
《SUN认证JAVA程序员考试大纲》是一份详细指导Java程序员准备SUN(Sun Microsystems,现已被Oracle收购)官方认证考试的重要文档。这份大纲为学习者提供了清晰的学习路径和重点,帮助他们了解考试涵盖的各个领域,...
Sun公司程序员认证(JAVA)笔试题 本资源涉及到 Java 编程语言的各种知识点,包括基本数据类型、运算符、控制流语句、方法调用、StringBuffer 等。 1. 在题目 11 中,我们看到了一些位运算符的应用。位运算符是 ...
原版CHM文档,内容完整
《Sun认证Java2程序员考试辅导(下册)》是一份专为准备Java程序员考试的考生量身定制的参考资料。这份资料旨在帮助考生深入理解和掌握Java编程语言的核心概念,提升编程技能,以顺利通过Sun认证Java2程序员考试。下面...
《SUN认证JAVA程序员考试大纲》是一份详细指导学习者准备SUN认证JAVA程序员考试的文档。这份大纲旨在帮助学员全面掌握Java编程的核心知识和技能,以顺利通过考试。 课程目标明确,旨在培养学员理解并掌握面向对象...
SUN公司JAVA程序员SCJP证书考试真题(绝对真题)!
《SCJP(Sun认证Java程序员)考试宝典1》是一本专门为准备SCJP认证考试的学员量身定制的复习资料。SCJP,全称为Sun Certified Java Programmer,是Oracle公司(原Sun Microsystems)推出的针对Java编程语言的基础级...
Sun_Java程序员认证考试题.doc
1. **Java概述**:Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems开发,后来被Oracle公司收购。Java设计为具有可移植性、安全性、健壮性和平台无关性的特点。 2. **数据类型**:Java中的数据类型分为...
2019年sun java程序员认证考试题库,这是今年的最新版与旧版有很大的区别可以方便复习、准备考试
Java则是一种完全面向对象的编程语言,由Sun Microsystems(现已被Oracle收购)推出。它具有“一次编写,到处运行”的跨平台特性,因为它的运行依赖于Java虚拟机(JVM)。Java被广泛应用于企业级应用开发、Web应用、...
Sun认证Java 2程序员考试是Java开发者们证明自身技能和专业知识的重要途径,它涵盖了Java语言的基础到高级特性,包括语法、面向对象编程、异常处理、多线程、输入/输出流、集合框架以及JVM工作原理等多个方面。...