我正在使用OpenCV的java包装器.我试图在电影的帧上写一个迭代器.我的问题是迭代器是一个巨大的内存泄漏.这是迭代器的一个非常简化的版本,它有这个漏洞:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
我使用这个循环迭代这个代码:
final VideoCapture vc = new VideoCapture("/path/to/file");
final SimpleIt it = new SimpleIt(vc);
while (it.hasNext) {
it.next();
}
Run Code Online (Sandbox Code Playgroud)
只是迭代会增加线性内存消耗.我看到问题是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;
}
Run Code Online (Sandbox Code Playgroud)
但是现在迭代器给出的最后一帧将被覆盖.因此,如果我对这个单帧感兴趣,我不能把它放在外面供以后使用.当然,我可以复制它,但这也很昂贵.
我假设问题是垃圾收集器不会破坏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++;
}
Run Code Online (Sandbox Code Playgroud)
使用迭代器的第二个版本,10.png和30.png将是不同的图像.但这显然不是预期的.
luk*_*ukk 15
你应该打电话mat.release().
在我的应用程序中,我遇到了类似的问题.帧速率非常高,以至于java堆长大到可用的总系统内存,这有时会导致JVM崩溃.GC太慢了,我没有任何机制来检查可用内存并等待,如果这还不够.
一种解决方案是通过简单地使用来降低帧速率,Thread.sleep()这当然似乎是不可接受的.但它帮助GC按时完成了它的工作.
最后使用mat.release()修复问题.
您不必担心Mat对象的垃圾收集,因为此调用仅释放基础数据.当正确的时间到来时,Java对象包装器将由GC处理.
Poy*_*yan 15
当我编写一个运行很长时间的应用程序时,我只想添加我的0.02美元.
当Java Mat-wrapper被垃圾收集时,会自动调用Mat.release().但是,由于Java包装器与本机分配的对象相比非常小,因此可能不会足够快地进行垃圾收集.
因此,您可以Mat.release()在知道自己已完成某个对象或System.gc()定期调用以强制删除未使用的对象时执行此操作.
的System.gc(); 不适合我.
我添加了这一行:
System.runFinalization();
代码邮箱:
startGC--;
if (startGC==0) {
System.gc();
System.runFinalization();
startGC=100;
}
Run Code Online (Sandbox Code Playgroud)
看来,没有好的解决方案.我现在已经试验了好几个小时.我想出的最好的是定期调用垃圾收集器,如下所示:
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;
Run Code Online (Sandbox Code Playgroud)
由于这有效,它表明我的假设是正确的,java无法识别从C分配的RAM,因此即使机器的RAM用完,也不会调用GC.
这不是一个很好的解决方案,因为它可能不是很稳定.如果其他人有更好的想法,我感兴趣.
小智 1
我会将你的.hasNext方法修改为:
public boolean hasNext() {
return hasNext;
}
Run Code Online (Sandbox Code Playgroud)
然后您描述的方法(在下面复制)应该可以正常工作...您将迭代直到什么都没有剩下,此时您可以将最后一个图像分配给一个新的 Mat 对象...
public Mat next() {
capture.retrieve(mat);
hasNext = capture.grab();
return mat;
}
Run Code Online (Sandbox Code Playgroud)
进而:
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();
}
Run Code Online (Sandbox Code Playgroud)
我确实意识到这会产生额外的内存使用量。可能有一种方法可以解决这个问题,但它应该可以正常工作......
| 归档时间: |
|
| 查看次数: |
6888 次 |
| 最近记录: |