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

时间:2017-07-11 05:56:26

标签: java garbage-collection ram

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

方法getImage()每100毫秒调用一次

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

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;
}

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

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;
}

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

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

在第二个代码块中,似乎代码没有第一个代码那么优化,但是效果比第一个代码好。

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

1 个答案:

答案 0 :(得分:1)

这是一种推测,虽然我在实际现场几次都看到了类似的情况。

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

终结器依靠垃圾收集器来触发它们,这就是模式经常失败的原因。虽然终结器最终可以保证最终运行,但实际上只要Java堆中有足够的空间并且本地内存不能及时解除分配,就不会发生这种情况。

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

关于您的问题

我假设在您的第一个片段中,Java堆流量很低。 GC处于空闲状态,并且没有执行终结器,因此在Java堆外部分配的内存不断增长。

在第二个片段中,您正在对Java堆施加压力,迫使GC频繁运行。执行GC终结器的副作用并释放本机内存。

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

<强>建议

  • -XX:+PrintGCDetails-XX:+PrintReferenceGC选项会打印有关参考处理的信息,因此您可以验证是否确实使用了终结器/幻像引用。
  • 查看相机的SDK文档,了解是否可以通过API尽早发布原生资源
  • 选项-XX:MaxDirectMemorySize=X可用于限制直接缓冲区使用,如果您的camara SDK继续使用它们。虽然它不是一个解决方案,但是在操作系统内存耗尽之前让你的应用程序OOM耗尽的安全网
  • 强制每隔几帧(例如System.gc())。这是另一个糟糕的选择,因为System.gc()的行为依赖于JVM。

PS
This is my post关于使用终结器和幻像引用的资源管理。