Java内存不足异常

时间:2009-11-30 09:15:57

标签: java tomcat xml-parsing quartz-scheduler jdom

我在Tomcat中运行Java Web应用程序。该应用程序使用Quartz框架定期调度cron作业。这个cron工作涉及解析4+ MB xml文件,我正在使用JDOM API。 xml文件包含大约3600个要解析的节点,因此要在DB中更新数据,我按顺序执行此操作 在解析了几乎一半的文件后,我的应用程序抛出了Out of Memory Exception。它的堆栈跟踪是:

Exception in thread "ContainerBackgroundProcessor[StandardEngine[Catalina]]" java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOfRange(Arrays.java:3210)
        at java.lang.String.<init>(String.java:216)
        at java.lang.StringBuffer.toString(StringBuffer.java:585)
        at org.netbeans.lib.profiler.server.ProfilerRuntimeMemory.traceVMObjectAlloc(ProfilerRuntimeMemory.java:170)
        at java.lang.Throwable.getStackTraceElement(Native Method)
        at java.lang.Throwable.getOurStackTrace(Throwable.java:590)
        at java.lang.Throwable.getStackTrace(Throwable.java:582)
        at org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:155)
        at org.apache.juli.logging.DirectJDKLog.error(DirectJDKLog.java:135)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1603)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1610)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1590)
        at java.lang.Thread.run(Thread.java:619)
Exception in thread "*** JFluid Monitor thread ***" java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:2760)
        at java.util.Arrays.copyOf(Arrays.java:2734)
        at java.util.Vector.ensureCapacityHelper(Vector.java:226)
        at java.util.Vector.add(Vector.java:728)
        at org.netbeans.lib.profiler.server.Monitors$SurvGenAndThreadsMonitor.updateSurvGenData(Monitors.java:230)
        at org.netbeans.lib.profiler.server.Monitors$SurvGenAndThreadsMonitor.run(Monitors.java:169)
Nov 30, 2009 2:22:05 PM org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor processChildren
SEVERE: Exception invoking periodic operation:
java.lang.OutOfMemoryError: Java heap space
        at java.lang.StringCoding$StringEncoder.encode(StringCoding.java:232)
        at java.lang.StringCoding.encode(StringCoding.java:272)
        at java.lang.String.getBytes(String.java:946)
        at java.io.UnixFileSystem.getLastModifiedTime(Native Method)
        at java.io.File.lastModified(File.java:826)
        at org.apache.catalina.startup.HostConfig.checkResources(HostConfig.java:1175)
        at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1269)
        at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:296)
        at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:118)
        at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1337)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1601)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1610)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1590)
        at java.lang.Thread.run(Thread.java:619)
ERROR [JobRunShell]: Job updateVendorData.quoteUpdate threw an unhandled Exception:
java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOfRange(Arrays.java:3210)
        at java.lang.String.<init>(String.java:216)
        at java.lang.StringBuffer.toString(StringBuffer.java:585)
        at org.apache.commons.dbcp.PoolingConnection$PStmtKey.hashCode(PoolingConnection.java:296)
        at java.util.HashMap.get(HashMap.java:300)
        at org.apache.commons.pool.impl.GenericKeyedObjectPool.decrementActiveCount(GenericKeyedObjectPool.java:1085)
        at org.apache.commons.pool.impl.GenericKeyedObjectPool.returnObject(GenericKeyedObjectPool.java:882)
        at org.apache.commons.dbcp.PoolablePreparedStatement.close(PoolablePreparedStatement.java:80)
        at org.apache.commons.dbcp.DelegatingStatement.close(DelegatingStatement.java:168)
        at com.netcore.smsapps.stock.db.CompanyDaoImpl.updateCompanyQuote(CompanyDaoImpl.java:173)
        at com.netcore.smsapps.stock.vendor.MyirisVendor.readScripQuotes(MyirisVendor.java:159)
        at com.netcore.smsapps.stock.update.StockUpdateData.execute(StockUpdateData.java:38)
        at org.quartz.core.JobRunShell.run(JobRunShell.java:207)
        at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:525)
DEBUG [ExceptionHelper]: Detected JDK support for nested exceptions.
ERROR [ErrorLogger]: Job (updateVendorData.quoteUpdate threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: java.lang.OutOfMemoryError: Java heap space]
        at org.quartz.core.JobRunShell.run(JobRunShell.java:216)
        at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:525)
Caused by: java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOfRange(Arrays.java:3210)
        at java.lang.String.<init>(String.java:216)
        at java.lang.StringBuffer.toString(StringBuffer.java:585)
        at org.apache.commons.dbcp.PoolingConnection$PStmtKey.hashCode(PoolingConnection.java:296)
        at java.util.HashMap.get(HashMap.java:300)
        at org.apache.commons.pool.impl.GenericKeyedObjectPool.decrementActiveCount(GenericKeyedObjectPool.java:1085)
        at org.apache.commons.pool.impl.GenericKeyedObjectPool.returnObject(GenericKeyedObjectPool.java:882)
        at org.apache.commons.dbcp.PoolablePreparedStatement.close(PoolablePreparedStatement.java:80)
        at org.apache.commons.dbcp.DelegatingStatement.close(DelegatingStatement.java:168)
        at com.netcore.smsapps.stock.db.CompanyDaoImpl.updateCompanyQuote(CompanyDaoImpl.java:173)
        at com.netcore.smsapps.stock.vendor.MyirisVendor.readScripQuotes(MyirisVendor.java:159)
        at com.netcore.smsapps.stock.update.StockUpdateData.execute(StockUpdateData.java:38)
        at org.quartz.core.JobRunShell.run(JobRunShell.java:207)

这会导致我的tomcat崩溃。你能帮我诊断一下这个问题。我甚至已经在Netbeans中启用了相同的分析,但似乎即使崩溃了。我保留了分配给Tomcat的默认内存。是否有任何内存泄漏发生。 我的DB是postgres,JDK是1.6.0_15。

谢谢, 阿米特

9 个答案:

答案 0 :(得分:10)

尝试增加JVM的ram分配。它应该有所帮助。

修复eclipse:你可以在eclipse偏好中配置它,如下所示

  1. Windows - &gt;偏好(在mac上:eclipse - &gt;偏好)
  2. Java - &gt;已安装的JRE
  3. 选择JRE并单击编辑
  4. 在默认VM参数字段中键入-Xmx1024M。 (或者你的记忆偏好,对于1 gb的ram是1024)
  5. 点击完成或确定。

答案 1 :(得分:5)

每次使用DOM来解析XML文件时,您都会将整个文件加载到内存中,而DOM基础结构将使用相同的大小来处理它,因此它将消耗大约两倍于文件大小的内存。

您需要使用SAX,一个基于事件的解析器。虽然这在第一次很难理解,但是它非常有效,因为它只保留在内存当前的解析节点中。

似乎Java有一些SAX实现,比如StAX,我希望它有所帮助。

答案 2 :(得分:3)

尝试增加JVM的ram分配。它应该有所帮助。

修复eclipse:你可以在eclipse偏好中配置它,如下所示

Windows - &gt;首选项(在Mac上:eclipse - &gt;首选项)Java - &gt;已安装的JRE选择JRE并单击默认VM参数字段中的编辑类型--Xms256m -Xmx512m -XX:MaxPermSize = 512m -XX:PermSize = 128m。 (或者你的记忆偏好,对于1 gb的RAM是1024)点击完成或OK。

答案 3 :(得分:2)

解析XML是一项相当昂贵的任务。平均DOM解析器已经需要至少次的内存空间,因为XML文档很大。你也应该考虑这个事实。为了确保在其他地方没有内存泄漏导致XML解析器的内存不足,您确实需要运行一个分析器。为它提供更多内存,将可用内存加倍并对其进行分析。当您确定原因并修复泄漏时,您可以回到“默认”内存并重新测试。或者,如果确实没有任何泄漏方法,那么只需给它一些比默认值更多的内存,以便它们都适合。

您也可以考虑使用更具内存效率的XML解析器,例如VTD-XMLhomepage herebenchmarks here)。

答案 4 :(得分:2)

您必须为tomcat JVM的PermGenSpace分配更多空间。

这可以使用JVM参数完成:-XX:MaxPermSize=128m

默认情况下,PermGen空间为64M(并且它包含所有已编译的类,因此如果类路径中有很多jar(类),则可能确实填充了此空间。)

在旁注中,您可以使用JVisualVM监控PermGen空间的大小,甚至可以使用YourKit Java Profiler检查其内容

答案 5 :(得分:1)

您是否尝试过设置最大堆大小以查看问题是否仍然存在?根本没有泄漏。可能只是默认堆大小(我认为Windows上64m)不足以满足此特定进程。

我发现我几乎总是需要给任何应用程序我运行Tomcat比默认值更多堆和perm gen空间,否则我将遇到内存不足的问题。如果您需要帮助调整内存设置,请查看this question

答案 6 :(得分:0)

你确定某处没有递归数组拷贝,错误地留在那里吗?也许在不同的线程中?

答案 7 :(得分:0)

关于文件和DOM会占用大量内存,我会说第二点。我也想知道我什么时候看到这个:

ERROR [JobRunShell]: Job updateVendorData.quoteUpdate threw an unhandled Exception:  
    java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOfRange(Arrays.java:3210)

复制是做什么的?我想知道你的代码中是否还有其他不良内容。

如果你已经做到这一点,它表明你已经成功读取了文件和DOM,并且你开始写入数据库了。应该已经回收了文件内存。

我建议使用VisualGC查看内存,以便了解正在发生的事情。

答案 8 :(得分:0)

您可以使用以下命令运行应用程序:-XX:+ HeapDumpOnOutOfMemoryError。这将导致JVM在内存不足时生成堆转储。您可以使用类似:MAT或JHAT来查看要保留的对象。我建议在生成的堆转储上使用eclipse内存分析器工具(MAT),因为它非常简单易用:http://www.eclipse.org/mat/

当然,您需要了解哪些对象可能会悬挂,以使其有用。 DOM对象?以前加载的xml文档的资源?数据库连接? MAT将允许您从一些您怀疑应该被垃圾收集的对象中追溯到根对象的引用。