7

我在 Tomcat 中运行 Java Web 应用程序。该应用程序使用 Quartz 框架定期安排 cron 作业。此 cron 作业涉及解析一个 4+ MB 的 xml 文件,我正在使用 JDOM API 进行此操作。xml 文件包含大约 3600 个要解析的节点,因此要在 DB 中更新数据,我正在按顺序进行。
在解析了几乎一半的文件后,我的应用程序抛出了内存不足异常。相同的堆栈跟踪是:

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 的默认内存。是否发生任何内存泄漏。我的数据库是 postgres,JDK 是 1.6.0_15。

谢谢,阿米特

4

9 回答 9

10

尝试增加 JVM 的内存分配。它应该有帮助。

修复 Eclipse:您可以在 Eclipse 首选项中进行如下配置

  1. Windows -> 首选项(在 mac 上:eclipse -> 首选项)
  2. Java -> 已安装的 JRE
  3. 选择 JRE 并单击编辑
  4. 在 -Xmx1024M 中的默认 VM 参数字段类型上。(或您的记忆偏好,对于 1 GB 的 ram 其 1024)
  5. 单击完成或确定。
于 2013-01-09T05:53:15.787 回答
5

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

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

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

于 2009-11-30T09:22:52.303 回答
3

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

您还可以考虑改用内存效率更高的 XML 解析器,例如VTD-XML(此处为主页,此处为基准测试)。

于 2009-11-30T11:31:43.843 回答
3

尝试增加 JVM 的内存分配。它应该有帮助。

修复 Eclipse

您可以在 Eclipse 的首选项中进行如下配置:

Windows -> 首选项(在 Mac 上是:Eclipse -> 首选项) Java -> 已安装的 JRE

选择 JRE 并单击默认 VM 参数字段类型上的编辑-Xms256m -Xmx512m -XX:MaxPermSize=512m -XX:PermSize=128m(或您的内存首选项,对于 1 gb 的 ram,它是 1024)。单击完成或确定。

于 2014-05-08T10:01:10.440 回答
2

You have to allocate more space to the PermGenSpace of the tomcat JVM.

This can be done with the JVM argument : -XX:MaxPermSize=128m

By default, the PermGen space is 64M (and it contains all compiled classes, so if you have a lot of jar (classes) in your classpath, you may indeed fill this space).

On a side note, you can monitor the size of the PermGen space with JVisualVM and you can even inspect its content with YourKit Java Profiler

于 2013-08-30T10:24:02.600 回答
1

您是否尝试将最大堆大小设置得更大以查看问题是否仍然存在?甚至可能根本没有泄漏。可能只是默认堆大小(我认为在 Windows 上为 64m)对于这个特定进程来说是不够的。

我发现我几乎总是需要为运行 Tomcat 的任何应用程序提供比默认设置更多的堆和永久生成空间,否则我会遇到内存不足的问题。如果您需要帮助调整内存设置,请查看此问题

于 2009-11-30T11:54:30.430 回答
0

I'll second that point about the file and the DOM taking up a great deal of memory. I also wonder when I see this:

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

What's that copying doing? I wonder if there's something else bad going on in your code.

If you've gotten this far, it suggests that you've read the file and the DOM successfully and you're starting to write to the database. The file memory should already be reclaimed.

I'd suggest looking at memory using VisualGC so you can see what's going on.

于 2009-11-30T11:02:52.953 回答
0

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

于 2009-11-30T09:23:58.693 回答
0

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

当然,您需要对可能悬挂的对象有所了解才能使其有用。DOM 对象?来自以前加载的 xml 文档的资源?数据库连接?MAT 将允许您从您怀疑应该已被垃圾回收的某个对象跟踪对根对象的引用。

于 2009-11-30T16:02:12.250 回答