2

3D キャンバスのスナップショットを保存できるようにするためCanvas3Dに、次の方法で拡張しました。

class OffScreenCanvas3D extends Canvas3D {
    OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration, boolean offScreen) {
        super(graphicsConfiguration, offScreen);
    }

    public BufferedImage doRender(int width, int height) {      
        BufferedImage bImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        ImageComponent2D buffer = new ImageComponent2D(ImageComponent.FORMAT_RGBA, bImage);
        setOffScreenBuffer(buffer);
        renderOffScreenBuffer();
        waitForOffScreenRendering();
        bImage = getOffScreenBuffer().getImage(); 
        setOffScreenBuffer(null);
        return bImage;
    }

    public void postSwap() {}
}

そして、それを宇宙へのビューとして追加します。そして、主な戦略はここで説明されています: http://www.java2s.com/Code/Java/3D/PrintCanvas3D.htm

問題はメモリリークにあります。アプリがクラッシュし始め、プロファイリングを試みたところ、 のインスタンスがOffScreenCanvas3D 50MBを占めており、その大部分が 2 つArrayListの から来ていることがわかりました。小さい方には のインスタンスが含まれ、大きい方にはとjavax.media.j3d.RenderMoleculeをそれぞれ含む Object のインスタンスが含まれます。javax.media.j3d.RenderAtomListInfojavax.media.j3d.RenderMolecule

私が間違っていることを誰かが私に提案できますか?

アップデート

doRenderがまったく呼び出されていない場合でも (たとえば、アプリケーションが起動され、それ以上のアクションが実行されていない場合)、メモリはまだ蓄積されていることを明確にしたいと思います。以下に、状況をよりよく示す画像を追加します。

1 つ目は、アイドル状態で実行中のアプリケーションのメモリ グラフです。 メモリーグリップ

2 つ目は、オブジェクトが使用するメモリの円グラフです。(a)のインスタンスに割り当てられるOffScreenCanvas3Dメモリは次のとおり(b)です。 は他のすべてのオブジェクトによって占有されるメモリです。 メモリ円グラフ

また、下にもそれが表示されdirtyDlistPerRinfoListdirtyRenderMoleculeListほとんどのスペースを占めています。接頭辞dirtyが付いているものはすべて、悪いコードのように感じます。理由はわかりません ここに画像の説明を入力

更新2

問題は次の部分にあるようです。

  1. のオブジェクトがクラスのメソッドにdirtyDlistPerRinfoList追加されます。これはすべてのキャンバスで発生します。updateCanvasResourceRenderBin
  2. dirtyDlistPerRinfoListクラスupdateDirtyDisplayListsのメソッドでクリアされます。RenderBin
  3. updateDirtyDisplayListsレンダリングされているすべての Canvas3D のdoWorkクラスで (恐ろしい 1300 行のメソッド)から呼び出されます。Renderer

問題は、offScreen キャンバスが常にレンダリングされているわけではなく、画像が保存される瞬間だけであるということです。はい、画像を保存した後、蓄積されたすべてのメモリdirtyDlistPerRinfoListが解放されます。

したがって、主な質問は次のとおりです。

のデータは、dirtyDlistPerRinfoListレンダリングされていないキャンバスに継続的に追加されるため、メモリは削除されません。これは Java3D バグの私のせいですか?

4

4 に答える 4

2

何もない。Java 3D アプリケーションには、25 MB 以上のメモリと、モデルを RenderMolecule および RenderAtomListInfo クラスに格納するために必要なメモリが必要です。BufferedImages もサイズによっては多くのメモリを消費することに注意してください。System.gc() を時々呼び出すと役立つ場合があります。BufferedImages を削除するのはそれほど簡単ではありません。

于 2012-12-04T11:05:47.380 に答える
1

あなたOffScreenCanvas3Dは一度だけ、doRender呼び出された後、Renderer.doWorkメソッドの対象です。をオーバーライドすることでこれを証明できますpreRender()。追加:

@Override
public void preRender() {
    System.out.println("OffScreenCanvas3D preRender !!!!!!!!!!!!!!!");
}

インスタンスのダーティ リストは後でクリアされ (前述のとおり)、次のスナップショット時に再利用されます。

名前が誤解を招く場合もあります。接頭辞「dirty」が付いたリストには、次のレンダリング ループに必要な更新を強制するオブジェクトが含まれています。

Java 3D は、レンダリング データからシーン グラフ データを分割するために、多くの冗長データを確実に作成します。とりわけ利点は、いつでも任意のユーザー スレッドで (ほぼ) 任意の Java 3D API メソッドを呼び出すことができることです。Java 3D コードをRunnables にカプセル化する必要はありません。これは、Swing と JavaFX の相互作用や他の構造に必要なためです。

于 2012-12-07T13:29:25.620 に答える
1

JDK ツール JConsole を使用すると、アプリケーションのメモリ使用量を監視できます。ガベージ コレクター (GC) を実行して、メモリが解放されているかどうかを確認できます。

プログラムで「java.lang.System.gc()」を呼び出すと、同じ効果があります。

Java 3D アプリケーション自体にはメモリ リークはありません。この投稿を書いている間、4-Canvas3D サンプル プログラム PropellerUniverse が実行されており、JConsole によって監視されています。JRE は数秒ごとにガベージ コレクションを自動的に開始します。ヒープ メモリの使用量は 25 MB、非ヒープ メモリの使用量は 34 MB です。プログラムは現在 25 分間実行されています。http://www.interactivemesh.org/testspace/j3dmeetsswing.html#heavyweight

于 2012-12-10T10:38:23.290 に答える
0

問題が発生する例にたどり着くまで、すべてのステップを排除するようにしてください。次の操作のみを行っても問題は発生しますか?

// Moved this to be a field
BufferedImage bImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
ImageComponent2D buffer = new ImageComponent2D(ImageComponent.FORMAT_RGBA, bImage);

OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration, boolean offScreen) {
    super(graphicsConfiguration, offScreen);
    // set this just once
    setOffScreenBuffer(buffer);
}

public BufferedImage doRender(int width, int height) {
    renderOffScreenBuffer();
    waitForOffScreenRendering();

    // not necessary, image does not change
    // bImage = getOffScreenBuffer().getImage(); 
    // why is this necessary?
    // setOffScreenBuffer(null);

    return bImage;
}
于 2012-12-04T14:33:55.633 に答える