Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Java: Memory leak from iterating OpenCV frames

I am using the java wrapper of OpenCV. I tried to write an Iterator over frames of a film. My problem is that the iterator is a huge memory leak. It fills a GByte within seconds. Here is a very simplified version of the iterator, which has this leak:

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

When I Iterate over this code using e.g. this loop:

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

memory consumption will increase linear. I see that the problem is the first line in the next()-Method. It always creates a new Mat. But speaking of java alone, this Mat will run out of scope as soon as the it.next() statement is over.

I could overcome the problem, by not using a new Mat every time, but overwriting always the same Mat-Object, like this:

    private final VideoCapture capture;
    private final Mat mat = new Mat();
    boolean hasNext;

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

But now the last frame which was given by the iterator will always be overwritten by the current frame. Thus, I cannot hold it outside for later use, if I am interested in this single frame. I could copy it, of course, but that would also be expensive.

To illustrate the problem with the last approach, imagin this code using the iterator:

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

With the second version of the iterator, 10.png, and 30.png will be different images. But that's obviously not what was intended.

I assume that the problem is that the garbage collector will not destroy the Mat objects, because it does not recognize the memory consumption, since it is not java heap space. Calling mat.release() in the loop will help, but of course in real code this means I will have no garbage collection for my Mat objects.

Anybody has an idea how to do it?

Java: Memory leak from iterating OpenCV frames

I am using the java wrapper of OpenCV. I tried to write an Iterator over frames of a film. My problem is that the iterator is a huge memory leak. It fills a GByte within seconds. Here is a very simplified version of the iterator, which has this leak:

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

When I Iterate over this code using e.g. this loop:

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

memory consumption will increase linear. I see that the problem is the first line in the next()-Method. It always creates a new Mat. But speaking of java alone, this Mat will run out of scope as soon as the it.next() statement is over.

I could overcome the problem, by not using a new Mat every time, but overwriting always the same Mat-Object, like this:

    private final VideoCapture capture;
    private final Mat mat = new Mat();
    boolean hasNext;

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

But now the last frame which was given by the iterator will always be overwritten by the current frame. Thus, I cannot hold it outside for later use, if I am interested in this single frame. I could copy it, of course, but that would also be expensive.

To illustrate the problem with the last approach, imagin this code using the iterator:

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

With the second version of the iterator, 10.png, and 30.png will be different images. But that's obviously not what was intended.

I assume that the problem is that the garbage collector will not destroy the Mat objects, because it does not recognize the memory consumption, since it is not java heap space. Calling mat.release() in the loop will help, but of course in real code this means I will have no garbage collection for my Mat objects.

Anybody has an idea how to do it?

Edit: I expermiented now a while with it, and came to the following solution:

    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;

If I call the garbage collector on a regular basis, it will delete all the unreferenced Mat objects, and together with them the RAM allocated from C. Well, that's probably a problem you have to face when mixing C and Java...

Java: Memory leak from iterating OpenCV frames

I am using the java wrapper of OpenCV. I tried to write an Iterator over frames of a film. My problem is that the iterator is a huge memory leak. It fills a GByte within seconds. Here is a very simplified version of the iterator, which has this leak:

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

When I Iterate over this code using e.g. this loop:

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

memory consumption will increase linear. I see that the problem is the first line in the next()-Method. It always creates a new Mat. But speaking of java alone, this Mat will run out of scope as soon as the it.next() statement is over.

I could overcome the problem, by not using a new Mat every time, but overwriting always the same Mat-Object, like this:

    private final VideoCapture capture;
    private final Mat mat = new Mat();
    boolean hasNext;

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

But now the last frame which was given by the iterator will always be overwritten by the current frame. Thus, I cannot hold it outside for later use, if I am interested in this single frame. I could copy it, of course, but that would also be expensive.

To illustrate the problem with the last approach, imagin this code using the iterator:

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

With the second version of the iterator, 10.png, and 30.png will be different images. But that's obviously not what was intended.

I assume that the problem is that the garbage collector will not destroy the Mat objects, because it does not recognize the memory consumption, since it is not java heap space. Calling mat.release() in the loop will help, but of course in real code this means I will have no garbage collection for my Mat objects.

Anybody has an idea how to do it?

Edit: I expermiented now a while with it, and came to the following solution:

    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;

If I call the garbage collector on a regular basis, it will delete all the unreferenced Mat objects, and together with them the RAM allocated from C. Well, that's probably a problem you have to face when mixing C and Java...

Edit2:

Ok from beraks comment I noticed that I failed huge in constructing a really small example. So here is a better one:

    final VideoCapture vc = new VideoCapture("/path/to/file");
    while (vc.grab()) {
        final Mat mat = new Mat();
        vc.retrieve(mat);
        doSomething(mat);
    }

This snipped will have the memory leak, too. If doSomething() wants to save some Mats away, we cannot get rid of the "new Mat()" line. Again, putting a System.gc() inside the loop helps.