web-dev-qa-db-ja.com

Sun.misc.Unsafe?を使用して、ByteBufferが割り当てたネイティブメモリを強制的に解放する例

JDKは、メモリがJavaヒープの外部に割り当てられる、いわゆるダイレクトByteBufferを割り当てる機能を提供します。このメモリはガベージコレクタによって影響を受けないため、これは有益です。 GCオーバーヘッド:これは、キャッシュなどの長寿命のもののプロパティに非常に役立ちます。

ただし、既存の実装には重大な問題が1つあります。基になるメモリは、所有するByteBufferがガベージコレクションされた場合にのみ非同期的に割り当てられます。早期の割り当て解除を強制する方法はありません。 GCサイクル自体はByteBufferの処理に影響されないため、これは問題になる可能性があります。ByteBufferは旧世代のメモリ領域に存在する可能性が高いため、ByteBufferが使用されなくなってから数時間後にGCが呼び出される可能性があります。

ただし、理論的には、Sun.misc.Unsafeメソッド(freeMemory、allocateMemory)を直接使用できるはずです。これは、JDK自体がネイティブメモリの割り当て/割り当て解除に使用するものです。コードを見ると、メモリが二重に解放される可能性があることが懸念されます。そのため、状態が適切にクリーンアップされることを確認したいと思います。

誰かが私にこれを行うコードを教えてもらえますか?理想的には、JNAの代わりにこれを使用したいと思うでしょう。

注:私は見ました この質問 これは一種の関連性があります。

指摘された答えが良い方法のようです: ここ は、このアイデアを使用するElasticSearchのコード例です。みんなありがとう!

21
StaxMan

割り当てられたネイティブメモリのベースアドレスはSun.misc.Unsafeコンストラクタのローカル変数であるため、Java.nio.DirectByteBufferを使用することはほとんど不可能です。

実際には、次のコードを使用してネイティブメモリを強制的に解放できます。

import Sun.misc.Cleaner;

import Java.lang.reflect.Field;
import Java.nio.ByteBuffer;

...

public static void main(String[] args) throws Exception {
    ByteBuffer direct = ByteBuffer.allocateDirect(1024);
    Field cleanerField = direct.getClass().getDeclaredField("cleaner");
    cleanerField.setAccessible(true);
    Cleaner cleaner = (Cleaner) cleanerField.get(direct);
    cleaner.clean();
}
4
szhem

メモリをクリーンアップするもっと簡単な方法があります。

public static void clean(ByteBuffer bb) {
    if(bb == null) return;
    Cleaner cleaner = ((DirectBuffer) bb).cleaner();
    if (cleaner != null) cleaner.clean();
}

直接またはメモリマップされたByteBufferをかなり迅速に破棄する場合、これを使用すると大きな違いが生じる可能性があります。

クリーナーを使用してこれを行う理由の1つは、基盤となるメモリリソースの複数のコピーを持つことができることです。スライス()で。クリーナーにはこれらのリソース数があります。

26
Peter Lawrey

基本的に必要なのは、IOストリームを使用するのと同じセマンティクスに従うことです。ストリームを一度閉じる必要があるのと同じように、メモリを一度解放する必要があります。したがって、独自のラッパーを作成できます。メモリの早期解放を可能にするネイティブ呼び出し

2
M Platvoet