Derby: Out Of Memory & Update

下面是一堆流水帐 慎入:

 

一. 问题

Java GC 监视方法与工具 中写道,我们的项目出现了Out Of Memory问题。

现在这个问题看来可能是由Derby引发的。

 

项目中使用的derby版本是10.1,而且还是beta版的。Liu同学google到的信息,这个版本有内存泄露:

Bug Fixes of Derby10.2.2

OutOfMemory error on continuous execution of SQL statement

 

10.2.2的release中说修复了这个bug。但由下列描述:

写道
After fixing the original memory leak, I still run into problems on repeated execution of a sql statement. Take the sample program in the bug and run it with a small heap size (4m). After around 80-90K executions an outofmemory error is thrown. I took snapshots of the heap while the program was running but couldn't find anything obviously wrong.
 

看来这个bug貌似修完了还有问题,需要测一下,因为我们要插入190k的数据一次。

 

二. Derby升级

为此我们做了一次Derby的升级。

我们的应用跑在SunAS 8里面,试了一下,按如下步骤升级:

1. 导出以前数据库的表结构和配置信息。

2. 使用10.4的derby创建新数据库。

 

在工程空间里面能够跑起来,可到SunAS 8里面便无法运行。抛出下面的异常:

 

写道
[#|2009-03-30T14:54:54.700+0800|严重|sun-appserver-pe8.0.0_01|javax.enterprise.system.container.web|_ThreadID=10;|WebModule[/psv2]Exception sending context initialized event to listener instance of class com.okidata.common.InitDB
java.lang.ExceptionInInitializerError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:141)
at com.okidata.common.DBConnectionManager.loadDrivers(DBConnectionManager.java:873)
at com.okidata.common.DBConnectionManager.init(DBConnectionManager.java:814)
at com.okidata.common.DBConnectionManager.<init>(DBConnectionManager.java:448)
at com.okidata.common.DBConnectionManager.<clinit>(DBConnectionManager.java:55)
at com.okidata.common.InitDB.contextInitialized(InitDB.java:76)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3659)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4120)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1089)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:760)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1089)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:467)
at org.apache.catalina.startup.Embedded.start(Embedded.java:986)
at com.sun.enterprise.web.WebContainer.start(WebContainer.java:490)
at com.sun.enterprise.web.PEWebContainer.startInstance(PEWebContainer.java:506)
at com.sun.enterprise.web.PEWebContainerLifecycle.onStartup(PEWebContainerLifecycle.java:54)
at com.sun.enterprise.server.ApplicationServer.onStartup(ApplicationServer.java:295)
at com.sun.enterprise.server.PEMain.run(PEMain.java:220)
at com.sun.enterprise.server.PEMain.main(PEMain.java:172)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.apache.commons.launcher.ChildMain.run(ChildMain.java:269)

Caused by: java.lang.SecurityException: Sealing violation loading org.apache.derby.jdbc.InternalDriver : Package org.apache.derby.jdbc is sealed.
at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1722)
at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:904)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1370)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1233)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302)
at org.apache.derby.iapi.jdbc.JDBCBoot.boot(Unknown Source)
at org.apache.derby.jdbc.EmbeddedDriver.boot(Unknown Source)
at org.apache.derby.jdbc.EmbeddedDriver.<clinit>(Unknown Source)
... 25 more

 

网上有说是load了两个derby.jar,

但我们调查了不是这个原因.

起初我们以为是class的版本不兼容, 以为是SunAS 8的jdk是1.4.0,而derby10.4要求1.5以上了。

 

但是看了一下Derby的官方文档, 10.2.2的Release Notes 有下列描述:

JDK/JDBC support:

* JDKs 1.3, 1.4, 1.5, and 1.6 plus J2ME J2ME/CDC/Foundation Profile
* JSR-169, JDBC 2.1, JDBC 3.0, and JDBC 4.0 support
 

看来这个版本不兼容的问题可以排除了.

 

回头我们再整理思路:

我们的程序有前后台之分, 我们所谓的前台是SunAS, 后台是我们自己的程序.

现在Derby的启动工作由前台来做, 后台使用网络连接的方式访问该数据库.

问题的原因可以肯定是什么地方不兼容了, 因为我们的前后台程序在工程空间中跑, 在tomcat中跑, 没有任何问题,

那么会不会是derby和SunAS不兼容? Liu同学再一次大胆而机敏的建议,

让后台启动derby, 前台通过网络的方式访问数据库如何.

 

尝试之, 正解是也!

 

nnd, 同样的方式, 启动同样Derby, 我们的后台(自己写的一个jar, 然后java -jar)启动没问题.

配置到web.xml里, 作为一个ServletContextListener去执行,我想不是ServletContextListener的限制,

因为在tomcat里面这么做是没有问题的,在contextInitialized可以很好的启动Derby

所以我们最后只好把它定义成"Sun Application Server 8的一个bug"

 

我们都被折腾得够呛, 因为从我们自己的bug, 折腾到derby的bug, 再折腾到SunAS的问题,

这一圈,确实有点绕.  不过还好, 总算成功的给他升了级, 接下来的便是测试结果.

 

 

三. 升级后测试结果.

升级到Derby10.2之后,有提高,但依旧Out Of Memory.

实际环境中, 每小时做一次19W的插入操作, 跑了两天, 他还是挂掉了.

因为堆大小使用的是默认64M, 所以也不确定是某些原因造成内存确实不够(如数据库变大之类), 还是有溢出.

反正也是升级, 还不如升到10.4正式版

 

升级到10.4之后

第一个晚上, Liu同学在他的电脑上,jprofile里,循环跑了180回左右, 搞出了个OOM, 我们很郁闷,

结果后来Liu同学发现这个OOM是jprofile的,

还好, 总算松了口气, 调大内存接着来~

 

让Derby单独一个虚拟机启动, 我们的应用使用默认64M堆大小, 数据库插入到12G左右, 跑了400来回

从jprofile的数据看, 没有内存溢出现象.

 

至此我们的问题总算算是圆满解决.

 

ps. 没有升级以前, 上述环境下内存溢出非常明显, 且与数据库大小有关系, 越大溢出越快, 这也是我们在另一台机器上的测试结果.

 

四. 总结.

Derby10.1在大批量持续插入时候, 有OOM的bug,

解决办法:

1. 优化我们的程序, 要是到对19W的ArrayList在19W的循环里,循环调用trimToSize可不是什么好事,

    即使每次都会remove掉一个元素,而这一切只是为了得到一个打印机列表.

2. 升级Derby.

3. 使用"-Xms256m -Xmx256m" 调大我们应用的堆大小.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值