Java内存不足错误: GC overhead limit exceeded when processing large text file - cant figure out how to improve performance

标签 java eclipse garbage-collection jvm

注意:我浏览了有关此问题的所有主题,我知道这通常取决于 JVM 设置和高效编码,但我不知道如何进一步改进。

我正在处理 CAIDA 网络拓扑的大型文本文件 (1GB),这基本上是整个互联网 IPv4 拓扑的转储。每行的格式为“节点大陆地区国家城市纬度经度”,我需要过滤所有重复的节点(例如每个节点具有相同的纬度/经度)。

我为具有相同地理位置的所有节点分配一个唯一的名称,并维护每个地理位置的 HashMap ->已经遇到的唯一名称。我还维护每个旧名称->唯一名称的 HashMap ,因为在下一步中我必须处理另一个文件,其中这些旧名称必须映射到每个位置的新唯一名称。

我用 Java 编写了这个,因为这是我所有其他处理发生的地方,但我收到了“超出 GC 开销限制”错误。下面是我正在执行的代码和错误日志:

        Scanner sc = new Scanner(new File(geo));
        String line = null;

        HashMap<String, String> nodeGeoMapper = new HashMap<String, String>(); // maps each coordinate to a unique node name
        HashMap<String, String> nodeMapper = new HashMap<String, String>(); // maps each original node name to a filtered node name (1 name per geo coordinate)

        PrintWriter output = new PrintWriter(geoFiltered);
        output.println("#node.geo Name\tcontintent\tCountry\tregion\tcity\tlatitude\tlongitude");
        int frenchCounter = 0;

        // declare all variables used in loop to avoid creating thousands of tiny objects
        String[] fields = null;
        String name = null;
        String continent = null;
        String country = null;
        String region = null;
        String city = null;
        double latitude = 0.0;
        double longitude = 0.0;
        String key = null;
        boolean seenBefore = true;
        String newname = null;
        String nodename = null;

        while (sc.hasNextLine()) {
            line = sc.nextLine();
            if (line.startsWith("node.geo")) {

                // process a line and retrieve the fields
                fields = line.split("\t"); // split all fields using the space as separator
                name = fields[0];
                name = name.trim().split(" ")[1]; // nodes.geo' 'N...
                continent = ""; // is empty and gets skipped
                country = fields[2];
                region = fields[3];
                city = fields[4];
                latitude = Double.parseDouble(fields[5]);
                longitude = Double.parseDouble(fields[6]);

                // we only want one node for each coordinate pair so we map to a unique name
                key = makeGeoKey(latitude, longitude);

                // check if we have seen a node with these coordinates before
                seenBefore = true;
                if (!nodeGeoMapper.containsKey(key)) {
                    newname = "N"+nodeCounter;
                    nodeCounter++;
                    nodeGeoMapper.put(key, newname);
                    seenBefore = false;
                    if (country.equals("FR"))
                        frenchCounter++;
                }
                nodename = nodeGeoMapper.get(key); // retrieve the unique name assigned to these geo coordinates
                nodeMapper.put(name, nodename); // keep a reference from old name to new name so we can map later


                if (!seenBefore) {
                //  System.out.println("node.geo "+nodename+"\t"+continent+"\t"+country+"\t"+region+"\t"+city+"\t"+latitude+"\t"+longitude);
                    output.println("node.geo "+nodename+"\t"+continent+"\t"+country+"\t"+region+"\t"+city+"\t"+latitude+"\t"+longitude);
                }

            }
        }
        sc.close();
        output.close();
        nodeGeoMapper = null;

错误:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.regex.Matcher.<init>(Unknown Source)
at java.util.regex.Matcher.toMatchResult(Unknown Source)
at java.util.Scanner.match(Unknown Source)
at java.util.Scanner.hasNextLine(Unknown Source)
at DataProcessing.filterGeoNodes(DataProcessing.java:236)
at DataProcessing.main(DataProcessing.java:114)

在执行过程中,我的 java 进程一直在 80% CPU 上运行,总共有 1,000,000K(大约)内存(笔记本电脑总共有 4GB)。输出文件有 59987 个唯一节点,因此这是 GeoLocation->Name HashMap 中键值的数量。我不知道 oldName->NewName HashMap 的大小,但这应该小于 Integer.Max_value,因为我的文本文件中没有那么多行。

我的两个问题是:

  • 如何改进代码以使用更少的内存或避免过多的 GC? (编辑:请保持 Java 7 兼容)

  • (已解决)我已阅读有关 JVM 设置(例如 -Xmx1024m)的线程,但我不知道 Eclipse IDE 中的何处可以更改这些设置。有人可以告诉我在哪里需要设置这些设置以及我可能想尝试哪些设置吗?

谢谢

已解决:对于有类似问题的人来说,问题在于nodeMapper hashmap,它必须存储3400万个String对象,这导致需要超过4GB的内存。我能够通过首先禁用 GC 阈值 -XX:-UseGCOverheadLimit,然后使用 -Xmx4gb 为我的 Java 进程分配 4GBRAM 来运行我的程序。处理它花了很长时间,但它确实有效,只是速度很慢,因为一旦 Java 达到 3-4GB RAM,它就会花费大量时间收集垃圾而不是处理文件。更强大的系统不会有任何问题。感谢您的帮助!

最佳答案

For the JVM arguments in Eclipse run configuration

您也可以尝试在运行时添加此选项: -XX:-使用GCOverheadLimit

Interesting explanation of this flag and your error message here

关于Java内存不足错误: GC overhead limit exceeded when processing large text file - cant figure out how to improve performance,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29766248/

相关文章:

java - 静态 block java中的常量问题

java - 为 2 种文件类型制定隐式规则

java - 如何在不创建实际对象的情况下访问类的已运行实例

java - 为基于 Java 的 Web 应用程序实现文章修订历史

java - 如何自动删除项目的所有system.out.println语句包括所有多行Sop语句

c# - 如果对象已被处置,抑制 gc 终结器是否可以节省一些时间?

java - 智能 watch 应用部署

java - 在 Eclipse 中每次运行之前关闭 Java 应用程序

java - 网络爬虫耗尽堆空间

c# - 委托(delegate)会导致内存泄漏吗? GC.TotalMemory(true) 似乎表明如此