16

JOGL の頂点情報を格納するためにダイレクト バッファ (java.nio) を使用しています。これらのバッファーは大きく、アプリケーションの存続期間中に数回置き換えられます。メモリが時間内に解放されず、数回交換した後にメモリが不足しています。

java.nio のバッファ クラスを使用して割り当てを解除する良い方法がないようです。私の質問はこれです:

JOGL でダイレクト バッファを削除する方法はありますか? glDeleteBuffer() を調べていますが、これはビデオ カードのメモリからバッファを削除するだけのようです。

ありがとう

4

6 に答える 6

21

直接 NIO バッファーは、管理されていないメモリを使用します。これは、Java ヒープではなく、ネイティブ ヒープに割り当てられることを意味します。結果として、JVM がネイティブ ヒープではなく Java ヒープでメモリ不足になった場合にのみ解放されます。言い換えれば、それは管理されていない = それらを管理するのはあなた次第です。ガベージ コレクションを強制することはお勧めできません。ほとんどの場合、この問題は解決しません。

直接の NIO バッファが役に立たなくなったことがわかったら、sun.misc.Cleaner (StaxMan が正しい) を使用してネイティブ メモリを解放し、clean() を呼び出し (Apache Harmony を除く)、free() を呼び出す必要があります。 (Apache Harmonyを使用)またはより良いパブリックAPIを使用してそれを行います(Java> 12では、AutoCloseableを拡張するAutoCleaningですか?)。

それを行うのはJOGLの仕事ではありません。プレーンなJavaコードを使用して自分で行うことができます。私の例は GPL v2 の下にあり、この例はより寛容なライセンスの下にあります。

編集:私の最新の例は、Java 1.9 でも動作し、OpenJDK、Oracle Java、Sun Java、Apache Harmony、GNU Classpath、および Android をサポートしています。Java < 1.7 (マルチキャッチ、ダイアモンド、およびジェネリック) で動作させるには、構文糖衣を削除する必要がある場合があります。

参照: http://www.ibm.com/developerworks/library/j-nativememory-linux/

Direct ByteBuffer オブジェクトは、ネイティブ バッファを自動的にクリーンアップしますが、これは Java ヒープ GC の一部としてのみ行うことができます。そのため、ネイティブ ヒープへの圧力に自動的に対応することはありません。GC は、Java ヒープがいっぱいになり、ヒープ割り当て要求を処理できない場合、または Java アプリケーションが明示的に要求した場合にのみ発生します (パフォーマンスの問題が発生するため、お勧めしません)。

参照: http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#direct

ダイレクト バッファの内容は、通常のガベージ コレクション ヒープの外に存在する可能性があります

このソリューションは Java 14 に統合されています。

try (MemorySegment segment = MemorySegment.allocateNative(100)) {
   ...
}

Java 16 では、 MemorySegment.ofByteBuffer(ByteBuffer)を呼び出してバイト バッファーをメモリ セグメントにラップし、そのメモリ アドレスを取得して解放できます (これは制限付きのメソッドです)。

CLinker.getInstance().freeMemoryRestricted(MemorySegment.ofByteBuffer(myByteBuffer).address());

通常、直接の NIO バッファーが ByteBuffer でない場合、割り当て解除できるバッファーを見つけるために、多くの重要なケースでリフレクションを使用する必要があることに注意してください。

注意: sun.misc.Cleaner は Java 1.9 のモジュール「java.base」のjdk.internal.ref.Cleanerに移動されました。後者は java.lang.Runnable を実装しました (その違いを思い出させてくれた Alan Bateman に感謝します)。短い時間ですが、もはやそうではありません。sun.misc.Unsafe.invokeCleaner() を呼び出す必要があります。これは JogAmp の Gluegen で行われます。私は、sun.misc.Unsafe に依存することを避けたので、Cleaner を Runnable として使用することを好みましたが、現在は機能しません。

私の最後の提案は、Java 9、10、11、および12で機能します。

私の最新の例では、インキュベーションされた機能 (Java >= 14 が必要) を使用する必要がありますが、非常に単純です。

Luceneには、より寛容なライセンスの下での良い例があります。

于 2014-11-06T10:41:11.640 に答える
3

ダイレクト バッファは扱いが難しく、通常のガベージ コレクションの保証がありません。詳細については、http: //docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#directを参照してください。

問題が発生した場合は、割り当てと割り当て解除を繰り返すのではなく、一度割り当ててバッファを再利用することをお勧めします。

于 2010-08-16T21:29:23.567 に答える
2

割り当て解除が行われる方法はひどいです-ソフト参照は基本的にCleanerオブジェクトに挿入され、ByteBufferを所有しているときに割り当て解除がガベージコレクションされます。しかし、これは実際にはタイムリーに呼び出されることが保証されていません。

于 2011-12-11T05:43:10.697 に答える
0

ダイレクト バッファの割り当て解除は、ByteBuffer オブジェクトがマークされてからしばらくしてからガベージ コレクタによって実行されるジョブです。

バッファへの最後の参照を削除した直後に gc を呼び出すことができます。少なくとも、メモリが少し速く解放される可能性があります。

于 2010-08-16T21:19:28.993 に答える
-1

非パブリック API のリフレクションを悪用するのではなく、サポートされているパブリック API 内で完全にこれを行うことができます。

malloc を NewDirectByteBuffer (順序を設定することを忘れないでください) でラップする JNI と、それを解放する類似の関数を記述します。

于 2016-05-19T06:33:12.667 に答える