代码之家  ›  专栏  ›  技术社区  ›  Dan Polites

在Java中通过Web服务下载大文件

  •  3
  • Dan Polites  · 技术社区  · 15 年前

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

    目前,我已经尝试使用SpringWS1.5.8和MTOM来将附件发送到客户机,但是我一直在处理内存不足的错误。我不认为这些错误与我的Tomcat6实例有关,因为我的服务器有8GB的内存,我将Tomcat配置为使用4GB内存。我在200MB大小的文件中发现这些错误。

    我需要使用SOAP,尽管它可能不是最好的方法。我希望在春天有一个解决方案,但如果不可能的话,我愿意接受其他的想法。我了解到,可以使用AXIOMSOAPMessageFactory将文件流式传输到服务器以进行上载,但不能反过来。这是真的吗?我使用的是Java 6。

    下面是我一直在SpringWS框架中得到的错误:

    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)
    
    3 回复  |  直到 15 年前
        1
  •  3
  •   Fedearne    15 年前

    它可能与你的伊甸园空间狭小有关。伊甸园空间是堆的一部分,在那里分配新对象,并一直保留,直到它们在GC中存活下来。 伊甸园的空间不大。(我没有默认值,但在1GB堆的默认设置中,它只有64MB)

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

    你可以尝试设置 -XX:NewSize=4196M 它将分配4GB的eden堆空间。

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

    你可以用 visualgc jvmstat 3.0 (不是与Java 5和6捆绑的分发)监视堆,以确定堆空间正在运行满。

    您可能还需要签出: Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine

    如果您解决了这个问题,您仍将面临性能低下的不可扩展解决方案。使用某种直接流媒体可能会更好。为此目的实现一个简单的servlet并不难。

        2
  •  2
  •   BalusC    15 年前

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

    您是否考虑过完全忘记SOAP接口的事情,然后使用java.net.urlconnection返回到基础知识并在此基础上进一步构建?通过这种方式,您可以使用fileoutputstream将inputstream直接写入磁盘,这比将整个内容存储在内存中更有效。

        3
  •  0
  •   Thorbjørn Ravn Andersen    15 年前

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

    如果您创建一个解析为要发送的实际文件并将其保留在该文件中的URL,您是否可以将其分散到Web服务器?