一、发现问题
PPM系统手工下发数据到CRM时。下发10万条,每次2000条分页下发。
时间长了会出现内存溢出。在测试库测试发现,linux的内存占用会上升到40%,然后weblogic抛出错误。
# java.lang.OutOfMemoryError: requested 67 bytes for char in /BUILD_AREA/jdk6_05/hotspot/src/share/vm/runtime/sharedRuntime.cpp. Out of swap space?
另外,在weblogic和tomcat下面测试,weblogic下面会出现这个问题,tomcat没有问题。另外把这句代码注释后,问题就不会出现了。Weblogic和tomcat的不同点在于weblogic用了分布式事务,tomcat是开发环境,没有用分布式事务。
二、分析问题
1、JVM参数调优
问题一定是和这个查询语句有关。但是这个SQL查询没有问题,dao也是单例的,对象也是方法内定义的,也不是静态的,应该会被回收:
看了weblogic的启动配置,于是通过打印GC来看一下问题,一开始发现打印的GC是这种格式:
352.403: [GC 352.404: [DefNew: 13487K->737K(14208K), 0.0121280 secs] 84892K->72466K(203264K), 0.0122890 secs] [Times: user=0.01 sys=0.01, real=0.01 secs] |
DefNew代表的意义是串行回收器,而这个应用是大吞吐量的应用,应该是并行回收才对。所以就改成了并行回收。
参考:
关于GC日志:http://blog.csdn.net/huangzhaoyang2009/article/details/11860757
关于Jvm内存调优jstat的使用:
http://www.cnblogs.com/jackyrong/archive/2010/01/21/1653163.html
新生代与老生代:http://blog.csdn.net/ustcxjt/article/details/7287430
详细的JVM内存模型、GC收集器:
http://blog.csdn.net/lufeng20/article/details/7402386
发现现网的配置是并发收集器,于是测试库也改成并发收集器,并把-xmx调用90,希望让内存早一点爆掉:
-XX:+UseConcMarkSweepGC:设置并发收集器 |
好处是加快了GC的频率。每秒都有年青代的GC,年老代要到90%左右才会GC。
[GC [ParNew: 14722K->1080K(14784K), 0.0080690 secs] 84352K->70971K(90560K), 0.0082290 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] [GC [1 CMS-initial-mark: 69891K(75776K)] 71242K(90560K), 0.0080590 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] [CMS-concurrent-mark: 0.262/0.262 secs] [Times: user=0.74 sys=0.02, real=0.26 secs] [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC [ParNew: 14264K->1167K(14784K), 0.0081860 secs] 84155K->71393K(90560K), 0.0083090 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] [CMS-concurrent-abortable-preclean: 0.014/0.719 secs] [Times: user=0.67 sys=0.04, real=0.72 secs] [GC[YG occupancy: 11369 K (14784 K)][Rescan (parallel) , 0.0128120 secs][weak refs processing, 0.0001920 secs] [1 CMS-remark: 70225K(75776K)] 81594K(90560K), 0.0132190 secs] [Times: user=0.04 sys=0.01, real=0.02 secs] [GC [ParNew: 14351K->1250K(14784K), 0.0069010 secs] 65693K->52592K(90560K), 0.0070140 secs] [Times: user=0.03 sys=0.01, real=0.01 secs] [CMS-concurrent-sweep: 0.286/0.294 secs] [Times: user=0.55 sys=0.02, real=0.29 secs] [CMS-concurrent-reset: 0.003/0.003 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC [ParNew: 14434K->1600K(14784K), 0.0209170 secs] 39510K->26930K(90560K), 0.0210620 secs] [Times: user=0.03 sys=0.01, real=0.02 secs] |
但是出问题时,年老代内存不到80% 说明不是年老代溢出了。
[GC [ParNew: 14740K->1513K(14784K), 0.0072830 secs] 66353K->53378K(90560K), 0.0073820 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] # # An unexpected error has been detected by Java Runtime Environment: # # java.lang.OutOfMemoryError: requested 52 bytes for unsigned char in /BUILD_AREA/jdk6_05/hotspot/src/share/vm/code/codeBlob.cpp. Out of swap space? # # Internal Error (allocation.inline.hpp:42), pid=26053, tid=3758812048 # Error: unsigned char in /BUILD_AREA/jdk6_05/hotspot/src/share/vm/code/codeBlob.cpp # # Java VM: Java HotSpot(TM) Client VM (10.0-b19 mixed mode linux-x86) # An error report file with more information is saved as: # /home/itop/domains/dist_domain/hs_err_pid26053.log pure virtual method called . 2、堆栈分析通过jmap命令把堆文布打出来,同时用eclipse的manager analyzer 工具进行分析,发现打印出来的堆只有14M,而且也没有发现问题。 3、关于native heap
重新把问题定位到报错的这句话:
是swap区内存不够用了,所以和jvm的堆没关系的。这时才想到之前说的内存都到了40%了,总内存8G,那说明是linux本身的内存爆了? 关于OutOfMemoryError:http://blog.csdn.net/sfdev/article/details/2051171
于是开始用top命令监控内存变化,发现可用内存不停的减少。这里top命令说明一下:
|
第一次:内存还有1.5G
第二次:内存还有0.8G
第三次:内存还有400M
这里发现一个问题,JVM内存最大只有90M,但是通过查看linux命令发现JVM占用了8G的48.8这么多内存。不会它真有用到native heap爆了?
发现网上有人同样遇到过这类情况:
内存怪兽:http://ayufox.iteye.com/blog/723896
网上也有人说jdbc驱动也是用到native heap
4、分布式事务
因为前面说到weblogic环境和tomcat环境的不同时有没有配分布式事务。所以就把weblogic下的分布式事务屏蔽。
屏蔽后还是有问题,然后,把屏蔽后的代码放到tomcat又没有问题。问题应该是weblogic环境,最后发现weblogic 配的数据源是
oracle.jdbc.xa.client.OracleXADataSource而tomcat是oracle.jdbc.OracleDriver
weblogic也改为oracle.jdbc.OracleDriver后正常。
看来真的是分布式事务的问题,用到的数据库驱动导致的。xa这个驱动为什么会导致这个问题还不知道。可能是没用好,或者分布式事务不适合这种影响。
是否是把数据都存到native heap中,为了后面分布式提交时可以比对?所以每每下发10万条数据,当下发5万条时就爆了。而爆了的内存与GC清理的内存差不太多。而分布式的提交是要等10万条全部执行完成。
三、处理问题
oracle的分布式数据库驱动的问题,暂时没有去分析。因为PPM的下发是可以把下发任务拆分成多个任务。例如下发到CRM和计费的任务,拆成分别下发到CRM、下发到计费。
所以可驱动修改,不会对业务有影响。
另外建议这种大数据量的分布式事务的控制可以通过记录每个子事务状态来控制。 如果子事务失败,通过重做的方式。无法回滚。
另外,如果是因为10万条语句是一个事务导致数据爆了,可以试着把事务放到2000条查询这个粒度。