【问题标题】:Java - OutofMemory Client/Server ApplicationJava - OutofMemory 客户端/服务器应用程序
【发布时间】:2014-04-18 22:58:13
【问题描述】:

我正在构建一个可以在远程计算机上运行的备份服务器。我认为现在的问题是我有内存泄漏。传输大文件时,过一会出现如下错误:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.lang.StringCoding$StringDecoder.decode(StringCoding.java:149)
    at java.lang.StringCoding.decode(StringCoding.java:193)
    at java.lang.StringCoding.decode(StringCoding.java:254)
    at java.lang.String.<init>(String.java:536)
    at java.lang.String.<init>(String.java:556)
    at Server.run(Server.java:61)
    at Server.main(Server.java:107)

我已经阅读了一些关于此的安静的文章,但没有一个真正给我一个关于如何解决这个问题的答案。代码在下面。提前致谢。

服务器:

private ServerSocket server;
private Socket acceptingSocket;

public Server(int port){
    try {
        server = new ServerSocket(port);
    } catch (IOException e) {
        System.out.println("Try again");
    }
}

/**
 * Reads all files and puts them in the back-up folder. Also creates the appropriate dirs.
 */
public void run(){
    BufferedInputStream buffer = null;
    DataInputStream reader = null;
    BufferedOutputStream out = null;
    DataOutputStream writer = null;
    int size = 0;
    try {
        acceptingSocket = server.accept();
        buffer = new BufferedInputStream(acceptingSocket.getInputStream());
        reader = new DataInputStream(buffer);
        out = new BufferedOutputStream(acceptingSocket.getOutputStream());
        writer = new DataOutputStream(out);
        size = reader.readInt();
    } catch (IOException e1) {
    }
    System.out.println("Size: " + size);
            //Variables I need later on, I thought this would help
    byte[] name;
    int fileNameLength = 0;
    long length=0;
    boolean dir  = false;
    int t = 0;
    String dirs = "";
    File direcs;
    File file;
    byte[] b;
    int bytes=0;
    for(int j = 0; j < size; j++){
        try {
            fileNameLength = reader.readInt();
            name = new byte[fileNameLength];
            reader.read(name, 0,fileNameLength);
            String path = new String(name);
            System.out.println("Path: " + path);
            length = reader.readLong();
            dir = reader.readBoolean();
            path = "/backup" + path;
            file = new File(path);
            if(!dir){
                t = file.getAbsolutePath().lastIndexOf("/");
                dirs = file.getAbsolutePath().substring(0, t);
                direcs = new File(dirs);
                System.out.println(direcs.mkdirs());
                FileOutputStream fos = new FileOutputStream(file);
                BufferedOutputStream bos = new BufferedOutputStream(fos);
                b = new byte[(int) length];
                bytes = reader.read(b, 0, (int)length);
                if(bytes != -1)
                    bos.write(b,0,(int)length);                 

                writer.writeUTF("File " + file.getAbsolutePath() + " is created!");
                writer.flush();
                bos.flush();
                fos.flush();
                bos.close();
                fos.close();
                out.flush();

            } else file.mkdirs();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    try {
        buffer.close();
        reader.close();
        out.close();
        writer.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public static void main(String[] args){
    int port = Integer.parseInt(args[0]);
    Server server = new Server(port);
    while(true)
        server.run();
}

客户:

private DataInputStream serverToClient;
private Socket client;
private DataOutputStream clientToServer;
private String name;

public Client(String name, int port){
    try {
        client = new Socket(name, port);
        //receive response server
        serverToClient = new DataInputStream(client.getInputStream());
        //send message to server
        clientToServer = new DataOutputStream(client.getOutputStream());
        this.name = name;
    }
    catch (IOException e) {
    }
}

/**
 * Closes all connections
 */
public void stop(){

    try {
        client.close();
        serverToClient.close();
        clientToServer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

/**
 * Sends all files and content of the files to the back-up server
 * @param filePath
 */
public void backUp(String filePath){
    ArrayList<File> files = new ArrayList<File>();
    boolean dir = false;
    long length = 0;
    byte[] buffer;
    try{
        listf(filePath, files, 0);
        System.out.println(files.toString());
        clientToServer.writeInt(files.size());

        for(File fi : files){
            dir = false;
            if(fi.isDirectory()) dir = true;
            clientToServer.writeInt(fi.getAbsolutePath().length());
            System.out.println(fi.getAbsolutePath().length());
            clientToServer.writeBytes(fi.getAbsolutePath());
            System.out.println(fi.getAbsolutePath());
            length = fi.length();
            clientToServer.writeLong(length);
            clientToServer.writeBoolean(dir);
            System.out.println("Dir? " + dir);
            System.out.println(length);
            if(!dir){
                FileInputStream fis = new FileInputStream(fi);
                BufferedInputStream bis = new BufferedInputStream(fis);

                buffer = new byte[(int)length];

                bis.read(buffer, 0, (int)length);
                clientToServer.write(buffer);
                System.out.println(serverToClient.readUTF());
                bis.close();
                fis.close();
                clientToServer.flush();
            }
        }

    } catch(IOException e){
        e.printStackTrace();
    }
}

/**
 * Get all files, folders and subfolders in the file specified by directoryName, recursively.
 * @param directoryName
 * @param files
 * @param size
 */
private void listf(String directoryName, ArrayList<File> files, int size) {
    File directory = new File(directoryName);
    size = files.size();
    if(size > 100){
        System.out.println(size);
        size += 100;
    }
    if(directory.isFile()) files.add(directory);
    else{
        File[] fList = directory.listFiles();
        for (File file : fList) {
            if(file.isDirectory() && file.listFiles().length == 0) files.add(file);
            else{
                if (file.isFile()) {
                    files.add(file);
                } else if (file.isDirectory()) {
                    listf(file.getAbsolutePath(), files, size);
                }
            }
        }
    }
}

public static void main(String[] args){
    String name = args[0];
    int port = Integer.parseInt(args[1]);
    System.out.println("Name: " + name + " Port: " + port);
    Client client = new Client(name, port);
    File file = new File(args[2]);
    if(file.exists())client.backUp(args[2]);
    else System.out.println("File doesn't exist");
    client.stop();

}

【问题讨论】:

    标签: java sockets memory-leaks out-of-memory


    【解决方案1】:

    您不需要文件大小的缓冲区。这只会浪费内存并增加延迟,而且显然它不能扩展到大文件。复制流的正确方法请参见this answer

    如果需要在传输后保持socket打开,需要事先知道长度,并且需要修改循环条件为

    while (total < length && (count = in.read(buffer, 0, length-total > buffer.length ? buffer.length : (int)(length-total))) > 0)
    

    其中'length' 是已知的文件长度,'total' 最初为零。您还必须在循环内将 'total' 增加 'count'。

    【讨论】:

    • 谢谢!我之前尝试过类似的事情,但没有让它安静下来。它现在可以工作了:)
    • 我还以字节数组的形式发送文件名,并每次在服务器端使用文件名的长度创建一个新的字节数组。这也会导致内存不足错误。我该如何解决这个问题?
    • 使用 DataOutputStream.writeUTF() 及其反例。
    • 我之前使用过,但后来出现错误:'java.io.UTFDataFormatException: malformed input around byte ...' 这是因为 read 和 writeUTF 使用修改后的 UTF-8 编码,那是导致问题..
    • @Rednas 仅当您在一端不使用 readUTF() 而在另一端不使用 writeUTF() 时。这些是 Java 中唯一能够理解彼此格式的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多