内存从迭代Opencv帧泄漏

我正在使用OpenCV的java包装器。 我试图在电影的帧上写一个迭代器。 我的问题是迭代器是一个巨大的内存泄漏。 这是迭代器的一个非常简化的版本,它有这个漏洞:

public static final class SimpleIt implements Iterator { private final VideoCapture capture; boolean hasNext; public SimpleIt(final VideoCapture capture) { this.capture = capture; hasNext = capture.grab(); } @Override public boolean hasNext() { return hasNext; } @Override public Mat next() { final Mat mat = new Mat(); capture.retrieve(mat); hasNext = capture.grab(); return mat; } } 

我使用这个循环迭代这个代码:

  final VideoCapture vc = new VideoCapture("/path/to/file"); final SimpleIt it = new SimpleIt(vc); while (it.hasNext) { it.next(); } 

只是迭代会增加线性内存消耗。 我看到问题是next() – Method中的第一行。 它总是创造一个新的垫子。 但是只谈到java,一旦迭代代码迭代到下一个图像,这个Mat就会用完范围。

我可以通过不每次使用新Mat来克服这个问题,但是总是覆盖相同的Mat-Object,如下所示:

  private final VideoCapture capture; private final Mat mat = new Mat(); boolean hasNext; @Override public Mat next() { capture.retrieve(mat); hasNext = capture.grab(); return mat; } 

但是现在迭代器给出的最后一帧将被覆盖。 因此,如果我对这个单帧感兴趣,我不能把它放在外面供以后使用。 当然,我可以复制它,但这也很昂贵。

我假设问题是垃圾收集器不会破坏Mat对象,因为它不识别内存消耗,因为它不是java堆空间。 在循环中调用mat.release()会有所帮助,但当然在实际代码中这意味着我的Mat对象没有垃圾收集。

有人知道怎么做吗?

编辑:

由于似乎不清楚我的第二个解决方案的问题是什么,我更明确地写下来了。 使用迭代器考虑以下代码:

  final VideoCapture vc = new VideoCapture("/path/to/file"); final SimpleIt it = new SimpleIt(vc); int i = 0; Mat save = null; while (it.hasNext) { final Mat next = it.next(); if (i == 10) { save = next; Highgui.imwrite("/path/to/10.png", save); } else if (i == 30) { Highgui.imwrite("/path/to/30.png", save); } i++; } 

使用迭代器的第二个版本,10.png和30.png将是不同的图像。 但这显然不是预期的。

你应该真的调用mat.release()

在我的应用程序中,我遇到了类似的问题。 帧速率非常高,以至于java堆长大到可用的总系统内存,这有时会导致JVM崩溃。 GC太慢了,我没有任何机制来检查可用内存并等待,如果这还不够。

一种解决方案是通过简单地使用Thread.sleep()来降低帧速率,这当然似乎是不可接受的。 但它帮助GC按时完成了它的工作。

最后使用mat.release()修复了问题。

您不必担心Mat对象的垃圾收集,因为此调用仅释放基础数据。 当正确的时间到来时,Java对象包装器将由GC处理。

当我编写一个运行很长时间的应用程序时,我只想添加我的0.02美元。

当Java Mat-wrapper被垃圾收集时,会自动调用Mat.release()。 但是,由于Java包装器与本机分配的对象相比非常小,因此可能不会足够快地进行垃圾收集。

因此,当您知道对象已完成或定期调用System.gc()以强制删除未使用的对象时,您可以执行Mat.release()

看来,没有好的解决方案。 我现在已经试了好几个小时。 我想出的最好的是定期调用垃圾收集器,如下所示:

  int count = 0; @Override public Mat next() { final Mat result = mat; mat = new Mat(); capture.retrieve(mat); hasNext = capture.grab(); if (++count % 200 == 0) { System.gc(); } return result; 

由于这有效,它表明我的假设是正确的,java无法识别从C分配的RAM,因此不会调用GC,即使机器的RAM用完了。

这不是一个很好的解决方案,因为它可能不是很稳定。 如果有其他人有更好的想法,我感兴趣。

的System.gc(); 不适合我。

我添加了这一行:

System.runFinalization();

代码邮箱:

  startGC--; if (startGC==0) { System.gc(); System.runFinalization(); startGC=100; } 

我会将你的.hasNext方法修改为:

 public boolean hasNext() { return hasNext; } 

然后你在下面复制的方法应该可以正常运行……你将迭代直到没有剩下任何东西,此时你可以将最后一个图像分配给一个新的Mat对象……

 public Mat next() { capture.retrieve(mat); hasNext = capture.grab(); return mat; } 

接着:

 final VideoCapture vc = new VideoCapture("/path/to/file"); final SimpleIt it = new SimpleIt(vc); final Mat lastFrame = new Mat(); while (it.hasNext) { lastFrame = it.next(); } 

我确实意识到这会产生额外的内存使用量 可能有一种方法,但它应该工作正常…