如何删除 Java OutOfMemoryError

How to remove Java OutOfMemoryError

我有一个程序可以获取非常大的 txt 数据并更改此 txt 数据中某些列的顺序。有关它的作用的更多详细信息,请参阅我的问题 。我使用带有地图的列表,我可以想象这对于 java 虚拟机来说太多了,因为 txt 文件有 400,000 个条目,但我不知道还能做什么。我用较小的 txt 文件尝试过,然后它工作正常。否则它会运行一个多小时然后我得到一个 OutOfMemoryError。

这是我的代码:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class Final {

public static void main(String[] args) {

    String path = "C:\\\\Users\\\\Ferid\\\\Downloads\\\\secdef\\\\secdef.txt";

    File file = new File(path);

    new Final().updateFile(file);
}

private void updateFile(File file) {

    List<String> allRows = getAllRows(file);

    String[] baseRow = allRows.get(0).split("\|");

    List<String> columns = getBaseColumns(baseRow);
    System.out.println(columns.size());

    appendNewColumns(allRows, columns);
    System.out.println(columns.size());

    List<Map<String, String>> mapList = convertToMap(allRows, columns);

    List<String> newList = new ArrayList<String>();

    appendHeader(columns, newList);

    appendData(mapList, newList, columns);

    String toPath = "C:\\\\Users\\\\Ferid\\\\Downloads\\\\secdef\\\\finalz2.txt";

    writeToNewFile(newList, toPath);

}

/**
 * Gibt alle Zeilen aus der Datei zurück.
 */
private static List<String> getAllRows(File file) {

    List<String> allRows = new ArrayList<>();
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new FileReader(file));
        String row = null;
        int i = 0;
        while ((row = reader.readLine()) != null) {
            allRows.add(row);

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

/**
 * Gibt die Hauptspalten aus der 1. Zeile zurück.
 */
private static List<String> getBaseColumns(String[] baseRow) {
    List<String> columns = new ArrayList<>();
    for (String rowEntry : baseRow) {
        String[] entry = rowEntry.split("=");
        columns.add(entry[0]);
    }
    return columns;
}

/**
 * Fügt alle neuen Spalten hinzu.
 */
private static void appendNewColumns(List<String> rows, List<String> columns) {
    for (String row : rows) {
        String[] splittedRow = row.split("\|");
        for (String column : splittedRow) {
            String[] entry = column.split("=");
            if (columns.contains(entry[0])) {
                continue;
            }
            columns.add(entry[0]);
        }
    }
}

/**
 * Konvertiert die Listeneinträge zu Maps.
 */
private static List<Map<String, String>> convertToMap(List<String> rows, List<String> columns) {
    List<Map<String, String>> mapList = new ArrayList<>();
    for (String row : rows) {
        Map<String, String> map = new TreeMap<>();
        String[] splittedRow = row.split("\|");
        List<String> rowList = Arrays.asList(splittedRow);
        for (String col : columns) {
            String newCol = findByColumn(rowList, col);
            if (newCol == null) {
                map.put(col, "null");
            } else {
                String[] arr = newCol.split("=");
                map.put(col, arr[1]);
            }
        }
        mapList.add(map);
    }
    return mapList;

}

/**
 * 
 */
private static String findByColumn(List<String> row, String col) {
    return row.stream().filter(o -> o.startsWith(col)).findFirst().orElse(null);
}

/**
 * Fügt die Header-Zeile in die neue Liste hinzu.
 */
private static void appendHeader(List<String> columns, List<String> list1) {
    String header = "";
    for (String column : columns) {
        header += column + "|";
    }
    list1.add(header + "\n");
}

/**
 * Fügt alle Daten in die entsprechenden neuen Dateien hinzu.
 */
private static void appendData(List<Map<String, String>> mapList, List<String> list1, List<String> columns) {
    for (Map<String, String> entry : mapList) {
        String line = "";
        for (String key : columns) {
            // for (String key : entry.keySet()) {
            line += entry.get(key) + "|";
        }

        list1.add(line + "\n");
    }
}

/**
 * Schreibt alle Werte in die neue Datei.
 */
private static void writeToNewFile(List<String> list, String path) {
    FileOutputStream out = null;
    try {
        out = new FileOutputStream(new File(path));
        for (String line : list) {
            out.write(line.getBytes());
        }
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}

您可以通过specyfying指定JVM可以使用的最大内存:-Xmx

例如。 -Xmx8G, 使用 M 或 G

我们无法就分配的内存量给出具体的建议,因为这在很大程度上取决于您的服务器设置、用户群的规模及其行为。您需要找到适合您的值,即没有明显的 GC 暂停,也没有 OutOfMemory 错误。

供参考,用于更改内存(堆)分配的 3 个最常用参数是:

  • Xms - the minimum size of the heap
  • Xmx - the maximum size of the heap
  • XX:MaxPermSize - the maximum size of PermGen (this is not used in Java 8 and above)

如果您决定增加内存设置,请遵循一些通用准则。

  • 以小增量增加 Xmx(例如一次增加 512mb),直到你不 不再遇到 OutOfMemory 错误。这是因为增加 超出服务器能力的堆足以垃圾 收集可能会导致其他问题(例如performance/freezing)
  • 如果你的错误是java。浪。 OutOfMemoryError : PermGen space, 以 256mb 的增量增加 -XX:MaxPermSize 参数,直到 错误停止发生。
  • 如果你的错误没有引用PermGen,就没有必要 增加它。简单的解释,PermGen是用来存储 类,并且通常大小相当静态,已被删除 在 Java 8. 更多信息请点击此处。考虑将 Xms 和 Xmx 设置为相同 值,因为这可以减少 GC 发生的时间,因为它会 不要尝试在每个集合上调整堆的大小。

如果您在 Windows 上启动 Confluence 作为服务,那么您不应该使用这些说明。相反,请参阅下面的 "Windows Service" 部分。

如果您是通过批处理文件启动 Confluence,您应该只遵循这些说明。 Confluence 作为服务启动时不使用批处理文件。

.bat 文件启动时在 Windows 安装中配置系统属性,

  1. 关闭 Confluence
  2. 从 /bin(独立)或 /bin(EAR-WAR 安装),打开 setenv.bat.
  3. 找到部分

CATALINA_OPTS="-Xms1024m -Xmx1024m -XX:+UseG1GC $CATALINA_OPTS" in Confluence 5.8 or above

CATALINA_OPTS="$CATALINA_OPTS -Xms1024m -Xmx1024m -XX:MaxPermSize=256m -XX:+UseG1GC" in Confluence 5.6 or 5.7

JAVA_OPTS="-Xms256m -Xmx512m -XX:MaxPermSize=256m in previous versions

  1. Xmx最大,Xms最小,MaxPermSize为PermGen。
  2. 启动 Confluence

在这种情况下,如果可能的话,逐行读取文件并分别处理每一行,而不是将整个文件保存在内存中是有意义的。

目前您的代码如下所示:

  1. 将所有行读入列表 L
  2. 为 L 中的每一行查找所有列
  3. 将 L 中的行转换为地图(在地图中使用 "null" 字符串而不是不设置值!!这可能是最后真正咬你的东西!)
  4. 将地图序列化为行

我调用 bs 只是为了增加可用内存,然后它稍后就会失败。您在这里遇到内存使用和性能方面的一般问题。让我提出一个不同的方式:

 1. for each line read (don't read the whole file at once!):
    1.1 find columns, collect in List C
 2. for each line read (again, don't read the whole file at once, do it as you read):
    2.2 for each column in C, write value if the row contains it, or null
    2.3 append to the result file (also don't keep the result in memory!)

有点像这样:

  BufferedReader reader = null;
  BufferedWriter writer = null;
    try {
        reader = new BufferedReader(new FileReader(file));
        String row = null;
        int i = 0;
        List<String> columns = new ArrayList<>();
        while ((row = reader.readLine()) != null) {
            columns.addAll(getColumns(row));

        }

        reader = new BufferedReader(new FileReader(file));
        writer = new BufferedWriter(new FileWriter(outFile));
        int i = 0;
        while ((row = reader.readLine()) != null) {
            writeRow(row, columns, writer);

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