3

我有一个 Web 应用程序,它是文件的存储库。此 Web 应用程序提供 Web 服务,允许客户端搜索存储库并通过 SOAP 下载任何附件。

目前我已经尝试使用带有 MTOM 的 Spring-WS 1.5.8 将附件发送到客户端,但我不断出现内存不足的错误。我不相信这些错误与我的 Tomcat 6 实例有关,因为我的服务器有 8GB 内存,而我将 Tomcat 配置为使用其中的 4GB。我在小至 200MB 的文件上遇到这些错误。

我需要使用 SOAP,即使它可能根本不是最好的方法。我更喜欢 Spring 中的解决方案,但如果那不可能,那么我对其他想法持开放态度。我读到可以使用 AxiomSoapMessageFactory 将文件流式传输到服务器以进行上传,但不能反过来。这是真的?我正在使用 Java 6。

这是我在 Spring WS Framework 中不断遇到的错误:

java.lang.OutOfMemoryError: Java heap space
    com.sun.xml.internal.messaging.saaj.util.ByteOutputStream.ensureCapacity(Unknown Source)
    com.sun.xml.internal.messaging.saaj.util.ByteOutputStream.write(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.BMMimeMultipart.find(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.BMMimeMultipart.readBody(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.BMMimeMultipart.getNextPart(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.BMMimeMultipart.parse(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.BMMimeMultipart.parse(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeMultipart.getCount(Unknown Source)
    com.sun.xml.internal.messaging.saaj.soap.MessageImpl.initializeAllAttachments(Unknown Source)
    com.sun.xml.internal.messaging.saaj.soap.MessageImpl.getAttachments(Unknown Source)
    org.springframework.ws.soap.saaj.Saaj13Implementation.getAttachment(Saaj13Implementation.java:305)
    org.springframework.ws.soap.saaj.SaajSoapMessage.getAttachment(SaajSoapMessage.java:226)
    org.springframework.ws.support.MarshallingUtils$MimeMessageContainer.getAttachment(MarshallingUtils.java:109)
    org.springframework.oxm.jaxb.Jaxb2Marshaller$Jaxb2AttachmentUnmarshaller.getAttachmentAsDataHandler(Jaxb2Marshaller.java:532)
    com.sun.xml.internal.bind.v2.runtime.unmarshaller.MTOMDecorator.startElement(Unknown Source)
    com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(Unknown Source)
    com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.scan(Unknown Source)
    com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(Unknown Source)
    com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(Unknown Source)
    javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:421)
    org.springframework.ws.support.MarshallingUtils.unmarshal(MarshallingUtils.java:62)
    org.springframework.ws.client.core.WebServiceTemplate$3.extractData(WebServiceTemplate.java:374)
    org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:560)
4

3 回答 3

3

这可能与您的伊甸园空间太小有关。伊甸园空间是堆的一部分,新对象被分配并一直保留到它们从 GC 中幸存下来。伊甸园空间不是很大。(我没有默认值,但在默认设置为 1GB 堆时只有 64MB)

您的文件可能会加载到伊甸园空间中。要么没有 200 MB 的可用空间,要么字节数组被分配到很小并且需要增长。数组在 Java 中抛出的唯一方法是分配一个新的更大的数组并执行 memcopy。这将从 100MB 增长到 200MB 显然需要 300 MB 的总伊甸园堆空间。

您可以尝试设置-XX:NewSize=4196M将分配 4GB 伊甸园堆空间。

我不得不说,我不知道我的 Tomcat 正在以某种服务器模式运行,它使用不同的 GC/堆策略。

您可以使用visualgcjvmstat 3.0(不是与 Java 5 和 6 捆绑的发行版)来监视堆并确定哪个堆空间已满。

您可能还想查看:使用 5.0 Java[tm] 虚拟机调整垃圾收集

如果你解决了这个问题,你仍然会面临性能不佳和不可扩展的解决方案。使用某种直接流式传输可能会更好。为此目的,实现一个简单的 servlet 应该不难。

于 2009-11-02T21:47:16.243 回答
2

Java 中的 SOAP/XML 总是有很多开销并且需要大量内存。在这种特定情况下,它试图在内存中分配一个(太大)字节 [],而不是直接将流写入另一种输出流(除了 ByteArrayOutputStream 之外的任何东西)。

您是否考虑过完全忘记 SOAP 接口的事情并使用 java.net.URLConnection 回到基础并进一步构建?这样,您可以使用 FileOutputStream 将 InputStream 直接写入磁盘,这比将整个内容存储在内存中更有效。

于 2009-11-02T19:50:21.837 回答
0

看起来您正在处理内存中的完整文件,而不是在将其发送到客户端时读取它。

如果您创建一个解析为要发送的实际文件的 URL 并将其留给它,您能否将其分散到 Web 服务器?

于 2009-11-02T20:45:45.980 回答