【发布时间】:2014-01-29 18:59:13
【问题描述】:
我正在使用 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;
}
}
我使用这个循环遍历这段代码:
final VideoCapture vc = new VideoCapture("/path/to/file");
final SimpleIt it = new SimpleIt(vc);
while (it.hasNext) {
it.next();
}
只是迭代会线性增加内存消耗。我看到问题出在 next()-Method 的第一行。它总是创建一个新的 Mat。但是单就 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 将是不同的图像。但这显然不是本意。
【问题讨论】:
-
内存不足了吗?如果没有,那么这不是泄漏 - GC 将在需要时运行。
-
是的,我这样做了。几秒钟后,一些 Gigabyte 已满。