2011-12-11 12 views
19

JDKは、メモリーがJavaヒープの外部に割り振られる、いわゆるダイレクトByteBuffersを割り当てる能力を提供します。このメモリはガベージコレクタには触れられておらず、GCオーバーヘッドには寄与しないので、これは有益です。これは、キャッシュなどのロングライフのプロパティにとって非常に便利です。ネイティブメモリを強制的に解放する例ByteBufferは、sun.misc.Unsafeを使用して割り当てましたか?

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

しかし理論的にはsun.misc.Unsafeのメソッド(freeMemory、allocateMemory)を直接使用することができます.JDK自体がネイティブメモリの割り当て/割り当て解除に使用するものです。 コードを見ると、私が目にする潜在的な懸念の1つは、メモリの二重解放の可能性です。そのため、状態が適切に浄化されるようにしたいと考えています。

誰も私にこれを行うコードを教えてもらえますか? JNAの代わりにこれを使用するのが理想的です。

注:私はthis questionを見ましたが、これは関連しています。

指摘されている答えが良い方法であるように見えます。hereは、アイデアを使用したElastic Searchのコード例です。みんなありがとう!

+0

これは何年も前のことですが、今見ています。 (過去に私はちょうど私のケースでは圧力を低く保っていたバッファーの循環キューを使用していました) –

+1

うわー、私はどこでも良い記事を見つけることができません:( –

+0

私はSystem.gcとにかくターゲットと思われるSun上では少なくとも無視されます – nilskp

答えて

5

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(); 
} 
+0

そうですね、これが私が尋ねた理由です。 DeallocatorへのコールバックとペアリングされたCleanerのオーケストレーション全体は複雑です。私は、このアプローチを試してみましょう。タイプクリーナーがアクセス可能であると仮定します(私はそれが呼び出されることが気づかない)。 – StaxMan

2

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

+0

はい、非常に高いレベルです - しかし、あなたはどのように正確にどのように詳細を見てきましたか?問題が起きる場所です:ByteBuffers、close()メソッドを使わないで直接行う方法はありません。残念ながら。 – StaxMan

+0

はい、安全でないものを使用して独自のバッファを実装してゲームを変更することができます。 (おそらく、誤った共有を避けるためにパッド入りメモリでうまく動作しますが、これはまったく別のボールゲームです) –

+0

十分に真です。そして、私の場合は、すべてのアクセスを完全に制御しているため、基になるrawストレージは公開されておらず、参照がぶら下がる危険もありません。 – StaxMan

25

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

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

直接またはメモリマップされたByteBufferをかなり迅速に破棄すると、大きな違いが生じます。

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

+1

Hmmh。私はクリーナーを見ていましたが、アクセス権が私にそれを直接呼び出させるかどうかは分かりませんでしたが、もしそうなら、これは間違いなく良い方法です。ありがとう - おそらく私はこのアプローチを破棄するにはあまりにも急いでいた! – StaxMan

+0

私はこのアプローチをいくつかのプロジェクトで使用してきました。反射イモを使用するよりも優れています。 –

+0

右 - クラスにアクセスできる限り、私は同意します。 Unsafe自体が欠けている可能性のあるケースをサポートする必要がある場合、コード自体を動的にロードすることができます。再度、感謝します! – StaxMan

関連する問題