3

我有工作的服务器和客户端应用程序,它们在发送小文件时工作得很好,但是当我尝试通过套接字发送例如 700mb 的电影文件时,它给了我

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space

我在网上搜索了一些关于发送大文件的教程,但不太明白,但我认为我的问题是在写文件。

这是服务器用来编写我的文件的代码:

output = new FileOutputStream(directory + "/" + fileName);
            long size = clientData.readLong();
            byte[] buffer = new byte[1024];

            while (size > 0 && (bytesRead = clientData.read(buffer, 0, (int) Math.min(buffer.length, size))) != -1) {
                output.write(buffer, 0, bytesRead);
                size -= bytesRead;
            }
            output.close();

这是我的客户用来发送文件的代码:

byte[] fileLength = new byte[(int) file.length()];  

        FileInputStream fis = new FileInputStream(file);  
        BufferedInputStream bis = new BufferedInputStream(fis);

        DataInputStream dis = new DataInputStream(bis);     
        dis.readFully(fileLength, 0, fileLength.length);  

        OutputStream os = socket.getOutputStream();  

        //Sending size of file.
        DataOutputStream dos = new DataOutputStream(os);   
        dos.writeLong(fileLength.length);
        dos.write(fileLength, 0, fileLength.length);     
        dos.flush();  

        socket.close();  
4

5 回答 5

7

它给你OutOfMemoryError是因为你试图在发送之前将整个文件读入内存。这是 100% 完全没有必要的。只需读写块,就像您在接收代码中所做的那样。

于 2012-07-12T10:02:48.567 回答
4

问题是您试图一次将整个文件加载到内存中(通过readFully),这超出了您的堆空间(我认为默认情况下为 256mb)。

你有两个选择:

  1. 不错的选择:以块的形式加载文件(例如,一次 1024 个字节)并像这样发送它。需要更多的努力来实现。
  2. 不好的选择:通过 -Xmx 选项增加你的堆空间。不推荐,我主要提到这一点,以防您需要更多的程序堆空间,但在这种情况下,这是一个非常糟糕的主意。

对于选项一,您可以尝试以下操作:

DataInputStream in = new DataInputStream(new FileInputStream("file.txt"));
byte[] arr = new byte[1024];
try {
    int len = 0;
    while((len = in.read(arr)) != -1)
    {
        // send the first len bytes of arr over socket.
    } 
} catch(IOException ex) {
    ex.printStackTrace();
}
于 2012-07-12T10:03:12.607 回答
2

OutOfMemoryError当您的程序达到堆大小限制时抛出。您将整个文件加载到 RAM 中。默认堆大小是物理内存大小的 1/4 或 1GB,所以你的程序达到了限制(你可能有 2GB RAM,所以堆大小是 512MB)。

您应该以块的形式读取文件(例如 10MB),这样您就不会达到堆大小限制,并且您可以在出现错误的情况下重新发送块,而不是重新发送整个文件。您甚至可以在不同的线程中读取块,因此当您发送 1 个块时,您可以加载第二个块,当您发送第一个块时,您可以立即开始发送第二个块。

于 2012-07-12T10:10:42.417 回答
1

问题是在您的客户端中,您为整个文件创建了一个字节数组。

byte[] fileLength = new byte[(int) file.length()];  //potentially huge buffer allocated here

您应该在服务器端执行相同的操作,并将文件块逐块读取到固定大小的缓冲区中。

于 2012-07-12T10:04:15.277 回答
0

就像您在服务器端使用缓冲区发送数据一样,在您的小程序/客户端中使用缓冲区来读取它。如果你正在传输一个大文件,当你使用readFully(). 此方法仅适用于您知道数据非常非常小的情况。

于 2012-07-12T10:05:44.637 回答