2

我的申请有问题,并缩小了一点。我的记忆中似乎有未使用的对象。

然而奇怪的是,GC 并没有收集它们(即使在程序基本上处于空闲状态时也没有,即没有处理线程处于活动状态),但是当我在 Netbeans IDE 的 Profiler 中按“收集垃圾”时,它确实收集了。

我找到了负责暂时使用大量内存的代码:

private Integer getWhiteLines(BufferedImage image) {
    Map<Integer, List<Color>> heightColors = new HashMap<>();

    for (int h = 0; h < image.getHeight(); h++) {
        List<Color> colors = new ArrayList<>();
        for (int w = 0; w < image.getWidth(); w++) {
            int colorRGBA = image.getRGB(w, h);
            Color color = new Color(colorRGBA, true);
            colors.add(color);
        }
        heightColors.put(h, colors);
    }

    Integer whiteLines = 0;
    for (Map.Entry<Integer, List<Color>> entry : heightColors.entrySet()) {
        Color avgColor = avgColor(entry.getValue());
        if (isWhite(avgColor)) {
            whiteLines++;
        }
    }
    return whiteLines;
}

它列出了图像(以前是 PDF 文件)中每个像素的颜色。

这个问题也相当大,例如在某些 PDF 中,Color对象会占用 14MB 的内存。

为什么会发生这种行为,我将如何解决它?

4

3 回答 3

1

没有明显的理由将colors每行的 ArrayList 保存到 Map 中,然后分别对其进行迭代以计算平均颜色并确定它是否是白线。您可以在一个循环中完成所有这些操作:

private Integer getWhiteLines(BufferedImage image) {
    Integer whiteLines = 0;
    for (int h = 0; h < image.getHeight(); h++) {
        List<Color> colors = new ArrayList<>();
        for (int w = 0; w < image.getWidth(); w++) {
            int colorRGBA = image.getRGB(w, h);
            Color color = new Color(colorRGBA, true);
            colors.add(color);
        }
        Color avgColor = avgColor(colors);
        if (isWhite(avgColor)) {
            whiteLines++;
        }
    }
    return whiteLines;
}

这将导致对象更早地超出范围(因为Color对象仅在外部循环的每次迭代中保留,而不是在函数的整个执行时间中保留),并且该内存可用于较早的垃圾收集.

但是,值得注意的是,垃圾收集器不一定会在您的程序空闲时运行。这不是一个免费的过程,因此垃圾收集仅在实际需要释放内存时才会发生。符合垃圾回收条件的对象并不意味着它的内存将立即被回收。

于 2013-10-14T12:32:38.710 回答
1

GC 不会在任何时候强制收集内存,仅当您需要分配内存并且您的 VM 没有足够的资源时。您可以通过以下方式减少占用空间:

private Integer getWhiteLines(BufferedImage image) {
    Integer whiteLines = 0;
    for (int h = 0; h < image.getHeight(); h++) {
        List<Color> colors = new ArrayList<>();
        for (int w = 0; w < image.getWidth(); w++) {
            int colorRGBA = image.getRGB(w, h);
            Color color = new Color(colorRGBA, true);
            colors.add(color);
        }

        Color avgColor = avgColor(colors);
        if (isWhite(avgColor)) {
            whiteLines++;
        }
    }

    return whiteLines;
}
于 2013-10-14T12:31:53.297 回答
0

你不能说当应用程序空闲时什么时候会收集垃圾内存。你有没有遇到过内存不足异常?此外,如果应用程序占用大量内存,您可以使用 -Xms -Xmx JVM 参数来限制它。

对于这种类型的任务,您应该考虑在迭代输入值时即时计算值。

如果不能即时执行,则应考虑重用首次创建的对象。

于 2013-10-14T12:31:35.283 回答