下载大数据时 HttpServer 上的 java.lang.OutOfMemoryError
我有 java 6 嵌入式 HttpServer。它有一个允许客户端下载大文本文件的句柄。问题是,当服务器同时有超过 10 个客户端时,我会出现内存不足异常。我很确定问题出在 Http Server 上。
HttpServer m_server = HttpServer.create(new InetSocketAddress(8080), 0);
m_server.createContext("/DownloadFile", new DownloadFileHandler() );
public class DownloadFileHandler implements HttpHandler {
private static byte[] myFile = new String("....................").getBytes(); //string about 8M
@Override
public void handle(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(HTTP_OK, myFile .length); OutputStream responseBody = exchange.getResponseBody();
responseBody.write(myFile );
responseBody.close();
}
}
现在我得到的异常是:
java.lang.OutOfMemoryError: Java heap space
at java.nio.HeapByteBuffer.<init>(Unknown Source)
at java.nio.ByteBuffer.allocate(Unknown Source)
at sun.net.httpserver.Request$WriteStream.write(Unknown Source)
at sun.net.httpserver.FixedLengthOutputStream.write(Unknown Source)
at java.io.FilterOutputStream.write(Unknown Source)
at sun.net.httpserver.PlaceholderOutputStream.write(Unknown Source)
at com.shunra.javadestination.webservices.DownloadFileHandler.handle(Unknown Source)
at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source)
at sun.net.httpserver.AuthFilter.doFilter(Unknown Source)
at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source)
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(Unknown Source)
at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source)
at sun.net.httpserver.ServerImpl$Exchange.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-24" java.lang.OutOfMemoryError:
有关 getBytes() 的建议不会改变异常。我试图保存对 byte[] 的静态引用,而不是每次都创建它。我仍然遇到同样的异常。
I have java 6 embedded HttpServer. It has a handle which allows clients to download a big text file. The problem is that whenthe server has more then 10 simultaneous clients, i get out of memory exception. I'm prety sure that the problem is around the Http Server.
HttpServer m_server = HttpServer.create(new InetSocketAddress(8080), 0);
m_server.createContext("/DownloadFile", new DownloadFileHandler() );
public class DownloadFileHandler implements HttpHandler {
private static byte[] myFile = new String("....................").getBytes(); //string about 8M
@Override
public void handle(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(HTTP_OK, myFile .length); OutputStream responseBody = exchange.getResponseBody();
responseBody.write(myFile );
responseBody.close();
}
}
Now the exception i get is:
java.lang.OutOfMemoryError: Java heap space
at java.nio.HeapByteBuffer.<init>(Unknown Source)
at java.nio.ByteBuffer.allocate(Unknown Source)
at sun.net.httpserver.Request$WriteStream.write(Unknown Source)
at sun.net.httpserver.FixedLengthOutputStream.write(Unknown Source)
at java.io.FilterOutputStream.write(Unknown Source)
at sun.net.httpserver.PlaceholderOutputStream.write(Unknown Source)
at com.shunra.javadestination.webservices.DownloadFileHandler.handle(Unknown Source)
at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source)
at sun.net.httpserver.AuthFilter.doFilter(Unknown Source)
at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source)
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(Unknown Source)
at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source)
at sun.net.httpserver.ServerImpl$Exchange.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-24" java.lang.OutOfMemoryError:
The suggestion regarding the getBytes() doesn't change the exception. i have tried to hold a static reference to byte[] instead of creating it each time. And I still get the same exception.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
不要对大文件这样做:
这效率低下,并且您需要堆空间来存储整个文件数据。当您第一次完全读取文件然后完全写入它时,您会浪费大量堆空间。
而是直接从文件中以特定大小的块读取/写入文件数据到响应。您可以自己编写代码,也可以仅使用 Apache Commons IO 中的 IOUtils 等实用程序类。
在写入文件之前不要先读取整个文件,这一点很重要。相反,要分块进行。在这里使用流并避免任何处理 byte[] 的东西,除了缓冲和小块之外。
编辑:
这是一些使用 Apache IO 的代码...
Do not do that for large files:
This is inefficient and you need heap space for storing the whole file data. You're wasting lots of heap space when you first read the file completly and afterwards write it completly.
Instead read/write the file data in chunks of specific size from file directly to the response. You can write code on your own or just use a utility class like
IOUtils
from Apache Commons IO.It is important to not read the whole file first before you write it. Instead do it in smaller chunks. Use streams here and avoid anything that deals with byte[] except for buffering and the small chunks.
Edit:
Here's some code with Apache IO...
如果您一次检索文件的所有字节,它必须将所有字节读入内存,然后将它们写入文件系统。尝试类似的东西:
If you retrieve all of the bytes for the file at once, it has to read all of them into memory and then write them to the filesystem. try something like:
使用流,这样您就不必一次写入所有数据。
请参阅 getRequestBody 和 getResponseBody。您需要将文件作为流打开并将字节写入适当的流。
Use streams so that you don't have to write all the data at once.
See getRequestBody and getResponseBody. You'll want to open your file as a stream and write the bytes to the appropriate stream.
对于此类大量数据,最好流式传输数据。流式传输意味着您以块的形式发送数据,而不是一次发送全部数据。这更加节省内存,因为您不必将所有数据存储在内存中,只需将其中的一部分存储即可。
此外,返回文件数据的更通用方法是使用常规的 InputStream 而不是 Reader。
InputStream
:用于读取任何类型的数据Reader
:用于读取文本数据使用
InputStream
意味着您不必担心字符编码。它还使您的代码更加灵活,因为它也允许您发送二进制文件。这是一个完整的解决方案:
With large amounts of data like this, it's best to stream the data. Streaming means that you send the data in chunks instead of sending it all at once. This is more memory-efficient because you don't have to store all the data in memory, just pieces of it.
Also, a more generic way of returning the file data is to use a regular
InputStream
instead of aReader
.InputStream
: used for reading any kind of dataReader
: used for reading text dataUsing an
InputStream
means you don't have to worry about character encodings. It also makes your code more flexible because it allows you to send binary files too.Here is a complete solution:
不要一次将整个字符串转换为字节:
Do not convert the whole String into bytes at once:
代码中的问题是
myFile.getBytes()
为每个请求创建一个新数组。您可以通过保存字节数组而不是字符串来简单地改进它:
顺便说一句,此代码和您的代码都使用
getBytes()
。这意味着它将使用默认的平台编码,这不是一个好的做法。最好使用显式编码来调用它,例如getBytes("UTF-8")
另请注意:我更正了您的代码,假设它是真实的代码。如果您的逻辑更复杂,例如您允许下载多个文件,最好使用流式传输:按块读取输入文件并将块发送到请求的位置。不要在内存中保留太多块。
The problem in your code that
myFile.getBytes()
creates a new array for each request.You can simply improve it by holding the byte array instead of String:
Btw, both this code and your code use
getBytes()
. This means that it will use the default platform encoding, which is not a good practice. It's better to call it with an explicit encoding, likegetBytes("UTF-8")
Another note: I corrected your code assuming it's a real code. In case your logic is more complex, e.g. you allow downloading multiple files, it's better to use streaming: read the input file by chunks and send the chunks to the requested. Don't keep too many chunks in memory.