【问题标题】:What is the reason for this method to take so much memory?这种方法占用这么多内存的原因是什么?
【发布时间】:2025-05-18 18:05:02
【问题描述】:

我正在尝试调试此方法,但似乎没有任何效果...
我怀疑在条件语句中 pathName.add() 会引起麻烦。执行此方法后,它会占用 50 MB,再执行一次它会占用 150 MB,直到达到 800 MB。但一切都是分配的空间。为什么 gc 不清理这个烂摊子???

P.s 这个方法根据给定的路径创建目录,这些路径是用条件语句构造的

P.s P.s 从 actionListener 中调用 writeDir(...) 方法(当点击 gui ic 上的按钮时)。按钮可以经常点击

P.s P.s P.s 我已经尝试了 Andreas 的建议,它部分工作。调用 pathName.clean() 后,eden 空间下降,但分配的空间仍在增长,已达到最大值。

会对您的意见感兴趣 :) 谢谢

调用 writeDir(...)

startButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
            try {
                        progress(0);
                        XMLSaxLogic stax = new XMLSaxLogic(xmlPath);

                        DetectionFilter detectionFilter = new DetectionFilter(stax.getObjets());
                        try {
                            WriteFile writeFile = new WriteFile(detectionFilter.getDetectionList());
                            writeFile.writeDir(savedDirPath, detectionFilter.getHardwareList(), stax.getSiteName());
                            progress(100);
                        } catch (Exception a) {
                            System.out.println(a.getLocalizedMessage());
                        }
                        GetFileCount getFileCount = new GetFileCount(savedDirPath, detectionFilter.getDetectionList(), combo.getSelectedIndex());

                        getFileCount.getFile(savedDirPath.getAbsoluteFile().toString());
                    } catch (Exception a) {
                        System.out.println(a.getLocalizedMessage());
                    }
}
}

writeDir(...)

   private ArrayList<String> detectList;
   private String detection = null;
   private String build = null;
   private Set<String> pathName = new LinkedHashSet<>();
   public WriteFile(ArrayList<String> detectList) {
       this.detectList = detectList;
   }
public void writeDir(File root, ArrayList<String> sevenElementList, ArrayList<String> oneElementList) {

    for (String site : oneElementList) {
        for (String s : sevenElementList) {
            int indexx = s.indexOf("_");
            int id = Character.getNumericValue(s.charAt(indexHardware - 1));

            for (String detectionList : detectList) {
                int index = detectionList.indexOf("_");
                int sId = Character.getNumericValue(detectionList.charAt(index + 1));

                if (detectionList.contains("Apple") && sId == id) {
                    detection = site.trim() + "/" + s + "/" + detectionList.trim();
                    pathName.add(format(detection));
                } else if (detectionList.contains("Banana") && sId == id) {
                    build = detection.trim() + "/" + detectionList.trim();
                    pathName.add(format(build.trim()));
                } else if (detectionList.contains("nananana") && sId == id) {
                    pathName.add(format(build.trim() + "/" + detectionList.trim()));
                } else if (detectionList.contains("Watermelone") && sId == id) {
                    pathName.add(format(build.trim() + "/" + detectionList));
                } else if (detectionList.contains("Orange") && sId == id) {
                    pathName.add(format(site.trim() + "/" + s.trim() + "/" + detectionList.trim()));
                }
            }
        }
    }
    createDirTest(pathName, root);
}
private void createDirTest(Set<String> pathArray, File root) {
    for (String s : pathArray) {
        File subdir = new File(root, s);
        subdir.mkdirs();
    }
}

private String format(String toBeFormated) {
    String toBeTrimmed = trimLastChar(toBeFormated.replace("ä", "ae").replace("ß", "ss").replace("ü", "ue").replace("ö", "oe").trim());
    return toBeTrimmed;
}

【问题讨论】:

  • 您介意发布调用 writeDir() 的代码吗?我怀疑您在循环过程中以某种方式修改了这些 for-each 列表中的一个(或多个)。但如果不查看调用该方法的位置/方式,我无法验证这种怀疑。
  • 是的,我可以做到:)秒
  • 还可以考虑替换 {site.trim() + "/" + s + "/" + detectionList.trim(); } 构造到StringBuilder 使用。在每次迭代调用 stringBuilderInstance.setLength(0) 后清除 StringBuilder。它将显着减少运行时分配的对象数量。
  • 感谢您的建议,我一定会这样做的。感谢您提醒我存在 stringbuilder :)

标签: java file loops directory-structure mkdirs


【解决方案1】:

您将所有路径存储在Set&lt;String&gt; pathName 字段中,即使您显示的部分代码仅将其用作createDirTest 的参数。

Set 使用的所有内存都不会被释放,直到您 1) 清除集合,或 2) 停止引用相关类的实例。

在某个未指定的未来时间点,JVM GC 将回收空间。

如果你真的需要它作为一个字段,为什么要作为参数传递? createDirTest 有权访问该字段。如果不是,请删除该字段,并声明一个局部变量。


更新

根据评论,该程序即使在使用了 800MB 后仍会继续运行。

在这种情况下,这就是 JVM 的工作方式。该程序可以正常运行,而您只是认为自己有问题。你没有。

您可以强制通过在actionPerformed() 方法末尾调用System.gc(),在try 语句之后 进行完整的垃圾回收,但仅用于确认完整的 GC 会释放所有使用的内存。仅用于测试,请勿将gc() 电话留在那里。

要查看 GC 运行情况,您可以在运行代码时添加 -Xloggc:path/to/file.log-XX:+PrintGCDetails -XX:+PrintGCTimeStamps 选项。有关更多详细信息,请参阅documentation。同样,只是为了测试。

【讨论】:

  • 好收获。只需将 pathArray.clear() 放在 createDirTest 的末尾即可验证。
  • 所以 iv 测试了它。问题仍然存在。尽管伊甸园空间下降,但分配的空间仍在增长,直到达到约 800MB。看到gc不想处理它
  • @Artiom 当它达到 800MB 时会发生什么?它会死吗?
  • 不,它只是保持在 800mb。我的mac有8gb,通常都用。我添加了一张图片来可视化情况。也许我如何调用这个方法有问题??
  • 我解决了分配问题。您的答案中描述了分区问题,但主要问题是在动作侦听器中定义的。每次点击都会创建新的对象。所以需要摆脱那里的新=,问题就解决了
【解决方案2】:

在意识到单击开始按钮创建了新对象的 WriteFile writeFile = new WriteFile(...)GetFileCount getFileCount = new GetFileCount(...)XMLSaxLogic stax = new XMLSaxLogic(...) 之后,我将 thouse 对象的创建移到了其他地方(那里只会创建一个!)。

现在没有内存问题,即使连续单击按钮也不会超过 150MB。一切看起来都很正常。但我确信代码中仍然存在一些引用问题,但由于调试主要问题已经消失:)

经验教训:避免过于频繁地创建太多对象^^

非常感谢@Andreas 和@ouflak !!

【讨论】: