运行此Java程序时,RAM会快速增加

运行此Java程序时,RAM会快速增加,java,garbage-collection,ram,Java,Garbage Collection,Ram,程序从IP摄像头接收以字节为单位的图像数据,然后处理图像。程序第一次启动时使用470Mb的RAM,每1秒就增加到15Mb,直到没有足够的空间,计算机挂断 方法getImage()每100ms调用一次 我做了一些实验,将在这里分享。原始代码是这样的:(其中缓冲区只创建一次,之后可以重用) 在上面的代码中,RAM上升到计算机挂断(99%10Gb)。然后我更改了如下代码:(在每个循环中,它将创建一个新的缓冲区) 在上面的代码中,RAM增加到大约43%(5Gb),然后释放 现在的问题是,第一块代码似乎得

程序从IP摄像头接收以字节为单位的图像数据,然后处理图像。程序第一次启动时使用470Mb的RAM,每1秒就增加到15Mb,直到没有足够的空间,计算机挂断

方法
getImage()
每100ms调用一次

我做了一些实验,将在这里分享。原始代码是这样的:(其中缓冲区只创建一次,之后可以重用)

在上面的代码中,RAM上升到计算机挂断(99%10Gb)。然后我更改了如下代码:(在每个循环中,它将创建一个新的缓冲区)

在上面的代码中,RAM增加到大约43%(5Gb),然后释放

现在的问题是,第一块代码似乎得到了优化,缓冲区可以重用,避免在每次调用中创建新的内存空间,但结果并不是我们想要的为什么?

在第二段代码中,似乎代码没有第一段优化,但比第一段工作得更好


但一般来说,为什么RAM在第一种情况下增加到10Gb,在第二种情况下增加到5Gb。我们怎样才能控制这种情况呢?

这只是一种猜测,尽管我在真实生活中见过几次类似的场景

您的Java代码正在与本机照相机SDK(dll)交互。本机代码类似于在非JVM内存中分配缓冲区,并使用一些内部Java对象访问该缓冲区。常见的(非常糟糕的)实践是,如果不再使用本机缓冲区,则依赖Java对象终结器释放本机缓冲区

终结器依赖于垃圾收集器来触发它们,这就是模式经常失败的原因。虽然终结器最终会运行,但实际上,只要Java堆中有足够的空间,并且本机内存不会被及时释放,终结器就不会运行

Java堆大小有硬限制,但只要操作系统允许,C/C++使用的本机内存池就可以增长

关于你的问题

我假设在您的第一个代码片段中,Java堆流量很低。GC是空闲的,不执行终结器,因此在Java堆之外分配的内存不断增长

在第二个代码段中,您正在对Java堆施加压力,迫使GC频繁运行。作为GC的一个副作用,finalizer被执行并释放本机内存

您的camera SDK可能会依赖Java直接内存缓冲区(这些内存是C代码的直接访问,因此可以方便地通过JVM边界传递数据),而不是在本机代码中分配终结器和缓冲区。虽然效果基本相同,因为Java直接缓冲区实现使用的是相同的模式(使用幻影引用代替终结器)

建议

  • 选项将打印有关引用处理的信息,以便您可以验证是否确实使用了终结器/幻影引用
  • 看看你们相机的SDK文档,看看是否有可能通过API提前发布本机资源
  • 选项可用于限制直接缓冲区的使用,前提是您的camara SDK依赖于它们。虽然这不是一个解决方案,但它是一个安全网,可以让您的应用程序在操作系统内存耗尽之前打开
  • 每隔几帧强制一次(例如,
    System.gc()
    )。这是另一个糟糕的选项,因为
    System.gc()
    的行为依赖于JVM
PS

关于使用终结器和幻影引用的资源管理。

您是否将JVM更新为最新版本?此代码中没有任何内容说明ram使用率如何或为什么会增加。你在用什么图书馆?你试过使用探查器吗?@NitishKumarDiwakar它已经是最新版本的java版本“1.8.0131”@matt是的,我使用的是IP摄像头SDK,它的C/C++代码有.dll和java接口。你可以设置最大内存,
-Xmx
,它将尝试保持较小的内存占用,如果超过这个值,则会出现OutOfMemoryError。Hmm,看来您已经找到了主要原因,它可以解决SDK的问题。但在这里,当我传递字节数组时,它也通过引用传递,而不是通过值传递,因此在SDK中的C/C++代码中可以获取此引用地址并放置数据,因为这个原因,我将缓冲区声明为全局(第一块代码)。但这不是我认为可以做到的事情。我不知道如何将字节数组传递给下划线C/C++代码?我认为在获取映像数据的过程中会有很多复制过程?堆上的Java对象无法传递给C/C++代码(只有少数非常特殊的例外)。C/C++代码可以通过API访问数组,这是相当低效的。Java可以创建特殊的“直接”ByteBuffer对象,该对象可以从Java访问,并且可以遵从C/C++内存指针。
private static final int WIDTH = 640;
private static final int HEIGHT = 480;
private byte[] sJpegPicBuffer = new byte[WIDTH * HEIGHT]; 

private Mat readImage() throws Exception {
    boolean isGetSuccess = camera.getImage(lUserID, sJpegPicBuffer, WIDTH * HEIGHT);
    if (isGetSuccess) {
        return Imgcodecs.imdecode(new MatOfByte(sJpegPicBuffer), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
    }
    return null;
}
private static final int WIDTH = 640;
private static final int HEIGHT = 480; 

private Mat readImage() throws Exception {
    byte[] sJpegPicBuffer = new byte[WIDTH * HEIGHT];
    boolean isGetSuccess = camera.getImage(lUserID, sJpegPicBuffer, WIDTH * HEIGHT);
    if (isGetSuccess) {
        return Imgcodecs.imdecode(new MatOfByte(sJpegPicBuffer), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
    }
    return null;
}