【问题标题】:why the code using Image.IO mallocs so much memory为什么使用 Image.IO malloc 的代码占用了这么多内存
【发布时间】:2019-10-02 16:00:59
【问题描述】:

我写了一个代码来检查ImageIO.read是否会占用大量内存,然后导致高内存使用。(之前有一个生产问题)

    import javax.imageio.ImageIO;

    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;


    public class ImageIOTest2 {

        public static void main(String[] args) {
                for (int i = 0; i < 20; i++) {
                    BufferedImage image;
                    try {
                        image = ImageIO.read(new File("test.jpg"));
                        System.out.println(image);
                        Thread.sleep(100);
                    } catch (IOException) {
                        e.printStackTrace();
                    }
                }
        }
    }

test.jpg 大约 4.3MB(我猜可能是额外的内存 4.3 * 20 *(3 或 4)不包括 JVM)。

我使用jemalloc 来跟踪内存分配。
LD_PRELOAD=/usr/local/jemalloc/lib/libjemalloc.so MALLOC_CONF=prof:true,lg_prof_sample:17,lg_prof_interval:25,prof_prefix:/root/output/je java -Xmx64m -Xms64m -XX:NativeMemoryTracking=summary ImageIOTest2

htop 中的 RSS 大约 160M,我打印了 NMT:

Native Memory Tracking:

Total: reserved=1377MB, committed=109MB
-                 Java Heap (reserved=64MB, committed=64MB)
                            (mmap: reserved=64MB, committed=64MB)

-                     Class (reserved=1037MB, committed=10MB)
                            (classes #827)
                            (malloc=5MB #716)
                            (mmap: reserved=1032MB, committed=5MB)

-                    Thread (reserved=16MB, committed=16MB)
                            (thread #17)
                            (stack: reserved=16MB, committed=16MB)

-                      Code (reserved=244MB, committed=3MB)
                            (mmap: reserved=244MB, committed=2MB)

-                        GC (reserved=8MB, committed=8MB)
                            (malloc=6MB #118)
                            (mmap: reserved=2MB, committed=2MB)

-                  Internal (reserved=5MB, committed=5MB)
                            (malloc=5MB #2080)

-                    Symbol (reserved=2MB, committed=2MB)
                            (malloc=1MB #200)
                            (arena=1MB #1)

已提交大约 109MB。

但是来自 jeprof 的总内存是 1055.7M:

如果我在代码中什么都不做,就打印一些东西。
它将 malloc 200MB。(在注释掉 ImageIO 和相同选项之前使用代码)。
因此,阅读 4.3MB jpeg 20 次几乎需要 800MB。
(Ps:只需使用 Files.readAllBytes 读取 jpeg 文件只需 malloc 256MB 使用相同的选项)

这正常吗?以及如何使用ImageIO优化内存?

【问题讨论】:

  • JPEG是压缩的(其实就是的压缩方式),Java肯定是不压缩保存图片的,也就是每个像素...所以像素计数很重要,而不是磁盘空间来确定使用了多少内存 - 相关:stackoverflow.com/q/12489009/85421
  • jeprof - 显示 77.9% 的内存(约 770 mb)用于解码图像像素映射。 IE。 ARGB 像素颜色分量数组(每个像素 4 个字节),它是 int 类型的数组。
  • @CarlosHeuberger thx~
  • @VictorGubin 因为堆没有使用额外的内存(大小固定)。所以本机库使用了大量内存?反正thx~
  • 据我所知,从java 1.4开始,BufferedImage默认光栅可以直接在video-ram中为图像像素图分配内存。在句柄模式下,它应该是对默认系统 VirtualAlloc/mmap 等的调用。

标签: java


【解决方案1】:

听起来差不多吧?让我们只计算字节数并记住垃圾收集是按增量进行的,而不是立即进行的。

循环使用的绝对最小内存数是:

图像宽度 * 图像高度 * 3(每个 RGB 字节;实际上更可能是 4)* 20 = 内存。

以上数字不依赖于技术。这只是处理的数据量。在 Java 中,只要有空闲内存可用,此内存就会累积(无法立即释放对象)。

既然你声称它在你的系统上占用了 800 MB,那么,向后工作,我猜:

宽 * 高 = 800 000 000 / 20 / 4 = 40 000 000 / 4 = 10 000 000。

这一切都意味着,如果您的源图像大约为 3500 x 2500 像素,那么您的代码几乎可以使用 800 MB。解决它的唯一方法是在丢弃每个图像后以某种方式调用 GC。例如,通过简单地限制可用内存量。

【讨论】:

  • 我觉得不是GC。因为Heap size是固定的,所以这个额外的内存是native memory malloc,看起来native lib用了这么多?
  • 坦率地说,我还是不太明白这个问题:您所描述的一切都是 100% 预期的:处理 800 MB 的数据使用 800 MB 的内存。你期待什么?你有什么问题?
  • 我的意思是 800M 不是在头部使用而是在堆外使用(不是直接内存而是本机内存)。这些区域不会发生 GC。而我的问题是如何优化内存(可能会更改一些本机库,例如使用 jemalloc 来代替原始 malloc)。 :)
  • 要么我不明白这个问题,要么你想多了,想找一个没有问题的地方。本机内存的 GC 由 Java 对象的 GC 驱动 - 内存是否为本机几乎没有区别。默认情况下,JVM 通过更频繁的收集更喜欢更大的内存使用来进行优化 - 如果您不喜欢它,您可能希望在丢弃每个图像后调用 GC。您也可以在使用图像后尝试在图像上调用刷新(这可能会更快地回收一些本机资源)。这可能只会影响您的微基准测试 - 在现实世界中 800MB 就是 800MB。
  • 这是一个生产问题。我发现内存没有被 GC 或 FGC 立即释放(我使用 jcmd 手动完成),但过了一段时间(可能是一个晚上,这真的很奇怪)。而且代码读取图像并行(我的同事已将其更改为使用工作队列)。无论如何我都想找到减少本机内存使用的读取图像(就像我之前说过的,更改本机图像库)。很抱歉没有说清楚并感谢您的帮助~ :)
【解决方案2】:

我在使用ImageIO.read() 时遇到了同样的问题。这导致生产服务器上的物理内存使用量增加,操作系统在该服务器上运行的 4 个 tomcat 实例中杀死了 3 个。

解决方案:使用 apache-commons-imaging 读取图像大小。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-13
    • 1970-01-01
    相关资源
    最近更新 更多