2014-01-10 72 views
11

我使用的是OpenCV的Java包装。我试图在电影的框架上编写一个迭代器。我的问题是迭代器是一个巨大的内存泄漏。这里是迭代器,它具有这种泄漏的一个非常简化的版本:内存从迭代Opencv帧泄漏

public static final class SimpleIt implements Iterator<Mat> { 

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

只是迭代会增加内存消耗的线性。我发现问题是下一个()方法的第一行。它总是创建一个新的垫子。但是单独谈论java,只要迭代代码迭代到下一个图像,该Mat就会超出范围。

我可以克服这个问题,不使用每当新垫,但总是覆盖相同垫对象,像这样:

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将是不同的图像。但这显然不是预期的。

+0

你运行内存不足?如果没有,那么这不是泄漏 - GC将在需要时运行。 –

+0

是的,我这样做。在几秒钟内,一些技嘉已满。 – Matthias

回答

1

我会修改你的.hasNext方法:

public boolean hasNext() { 
    return hasNext; 
} 

然后你所描述的方法,下面复制,应该可以正常工作......你会迭代,直到没有剩下,此时您可以分配最后一个图像到一个新的垫目标......

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

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

+0

这不是真正的解决方案,我担心。这样你就不能保存你用iteraor检索到的Mat。看我的编辑我的问题。 – Matthias

5

看来,没有好的解决方案。我现在进行了几个小时的实验。最好的我想出了打电话像这样定期的垃圾收集器:

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,这样做即使机器的RAM耗尽,也不要调用GC。

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

13

你应该真的打电话mat.release()

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

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

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

您不必担心Mat对象的垃圾回收,因为此调用仅释放底层数据。在适当的时候,Java对象封装将由GC进行处理。

+0

目前我的解决方案适用于我,尽管我对此感到非常不安。机器释放()的问题在于,确定一个餐具垫何时不能使用将是非常困难的。我做了大量的处理,将它们传递到系统的各个部分进行不同的分析。许多部分也不知道其他人是否也使用相同的垫子。这可能是java开发人员设计系统的方式。尽管> 99%的帧在第一个分析步骤后不会中断并丢弃,所以或许我应该至少释放这些帧。 – Matthias

+1

我刚想出一个想法,你可以在某种引用计数器对象中包装Mat对象。例如, 'acquire()'和'realese()'方法可以处理对“Mat”的活动引用计数。当这个计数为零时,你会调用'Mat.release()'来释放内存。 – lukk

+1

@lukk我正在改正,但有一点需要注意的是,Mat已经使用引用计数器来管理底层内存,因为opencv非常强烈地共享Mat引用:http://docs.opencv.org/java/2.4.11 /org/opencv/core/Mat.html - 我认为你可以创建一个新的Mat,它用'new Mat(m。getNativeObjAddr());' – CmdrDats

13

我只想添加我的$ 0.02,因为我在编写将长时间运行的应用程序时遇到了这个问题。

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

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

+2

这个策略似乎对我有用。 Java认为它像每个mat都像1 Object Reference和1 long一样分配。但是,C++方面做得更多。由于Java看不到那个内存,它不知道有多少内存可以玩。你必须强制调用'Mat.finalize()'。 'System.gc()'会做到这一点。 – HesNotTheStig

+0

当我定制openCV 3教程时,我不得不添加“modified.release();”这一行。紧跟在“Utils.matToBitmap(modified,mCacheBitmap);”之后在CameraBridgeViewBase.java文件中,现在所有运行稳定。建议我也在onCameraFrame中调用System.gc()。 – Logic1

3

System.gc();不适合我。

我有添加一行:

System.runFinalization();

一个Codesnippet:

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