2014-01-10 112 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(); 
    } 

だけ反復は、メモリ消費の線形が増加します。問題はnext() - Methodの最初の行であることがわかります。常に新しいMatを作成します。しかし、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; 
    } 

しかし、今意志イテレータによって与えられた最後のフレーム上書きされる。したがって、この1つのフレームに興味があれば、後で使用するために外部に保持することはできません。私はそれをコピーすることができます、もちろん、それはまた高価だろう。

Javaヒープスペースではないため、ガベージコレクタはメモリ消費を認識しないため、Matオブジェクトを破棄しないことが問題であると想定します。ループ内で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

良い解決策はないようです。私はかなりの時間の間、それを実験しました。これは動作するので、それは私の仮定が正しかったことを、JavaはCから割り当てられたRAMを認識し、ひいてはいないことを示し

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; 

:私はこのような定期的にガベージコレクタを呼んでいた思い付いた最高マシンのRAMが不足しているにもかかわらず、GCを呼び出さないでください。

非常に安定していない可能性があるため、これはあまり良い解決策ではありません。他の誰かが良いアイデアを持っているなら、私は興味があります。

13

実際にはmat.release()に電話する必要があります。

私のアプリケーションでは、あなたと非常に似た問題があります。フレームレートが非常に高いため、Javaヒープは使用可能なシステムメモリの合計に達したため、JVMがクラッシュすることがありました。 GCは単純に遅過ぎました。使用可能なメモリをチェックし、それが十分でないかどうかを確認するメカニズムはありませんでした。

単純にThread.sleep()を使用してフレームレートを低下させることが1つの解決策でしたが、これはもちろん受け入れられないようです。しかし、それはGCが時間通りに仕事をするのを助けました。

最後にmat.release()を使用して問題を修正しました。

Matオブジェクトのガベージコレクションについて心配する必要はありません。この呼び出しは、基礎となるデータのみを割り当て解除するためです。 Javaオブジェクトラッパーは、適切な時期にGCによって廃棄されます。

+0

現在、私のソリューションは私のために働いていますが、私はそれについて非常に不安を感じています。問題は 'release()'です。なぜなら、いつマットが使えなくなったのかを判断することは非常に難しいでしょう。さまざまな分析のために、それらをシステムのさまざまな部分に渡して、それらを処理します。多くのパーツは他のパーツが同じマットを使用しているかどうかわかりません。それはおそらく、Java開発者がシステムを設計する方法です。フレームの> 99%が最初の分析ステップの後に直接挿入され、破棄されますが、少なくともそれらをリリースする必要があります。 – Matthias

+1

私は 'Mat'オブジェクトをある種の参照カウンタオブジェクトにラップすることができるという考えを思いついたばかりです。例えば'acquire()'と 'realese()'メソッドを使うと、 'Mat'に対するアクティブな参照の数を扱うことができます。このカウントがゼロになると、 'Mat.release()'を呼び出してメモリを解放します。 – lukk

+1

@lukk私は訂正を話しますが、注意すべき点の1つは、OpenCVはMat参照を非常に激しく共有しているため、Matは既に基礎となるメモリを管理するために参照カウンタを使用していることです。http://docs.opencv.org/java/2.4.11 /org/opencv/core/Mat.html - 私は、新しいMatを使って古いものを参照する新しいMatを作ることができると思います。getNativeObjAddr()); ' – CmdrDats

13

長い間実行されるアプリケーションを作成するときに、この問題にぶつかり、$ 0.02を追加したいだけです。

Mat.Release()は、Java Mat-wrapperがガベージコレクトされると自動的に呼び出されます。ただし、Javaラッパーはネイティブに割り当てられたオブジェクトに比べて非常に小さいため、十分に高速にガベージコレクションされない可能性があります。

したがって、オブジェクトが作成されたことがわかった場合はMat.release()を実行するか、一定間隔でSystem.gc()を呼び出して、未使用のオブジェクトを強制的に削除することができます。

+2

この戦略は私のために働いているようです。 Javaは、1マットごとに1オブジェクト参照と1ロングのように割り当てていると考えました。しかし、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を();

A Codesnippet:

startGC--; 
    if (startGC==0) { 
     System.gc(); 
     System.runFinalization(); 
     startGC=100; 
    } 
関連する問題