32

不適切に破棄された直接バイト バッファーに分離したメモリ リークがあります。

ByteBuffer バフ = ByteBuffer.allocateDirect(7777777);

GC は、これらのバッファーを保持するオブジェクトを収集しますが、バッファー自体は破棄しません。バッファーを含む一時オブジェクトを十分にインスタンス化すると、次の励ましのメッセージが表示されます。

java.lang.OutOfMemoryError: ダイレクト バッファ メモリ

私はこの問題を調べていて、どうやら

buff.clear();

System.gc();

動作しない。

4

7 に答える 7

18

アプリケーションのどこかに ByteBuffer インスタンスへの参照があり、それがガベージ コレクションの妨げになっていると思われます。

ダイレクト ByteBuffer のバッファ メモリは、通常のヒープの外側に割り当てられます (GC が移動しないようにするためです!!)。ただし、ByteBuffer API には、バッファを明示的に破棄/割り当て解除する方法はありません。したがって、ガベージコレクターがそれを行うと思います... ByteBufferオブジェクトが参照されなくなったと判断したら。

于 2009-12-06T04:58:22.420 に答える
17

DBB は、参照キューに到達すると割り当てが解除され、ファイナライザーが実行されます。ただし、ファイナライザーの実行に依存できないため、リフレクションを使用して手動で「クリーナー」を呼び出すことができます。

リフレクションの使用:

/**
* DirectByteBuffers are garbage collected by using a phantom reference and a
* reference queue. Every once a while, the JVM checks the reference queue and
* cleans the DirectByteBuffers. However, as this doesn't happen
* immediately after discarding all references to a DirectByteBuffer, it's
* easy to OutOfMemoryError yourself using DirectByteBuffers. This function
* explicitly calls the Cleaner method of a DirectByteBuffer.
* 
* @param toBeDestroyed
*          The DirectByteBuffer that will be "cleaned". Utilizes reflection.
*          
*/
public static void destroyDirectByteBuffer(ByteBuffer toBeDestroyed)
    throws IllegalArgumentException, IllegalAccessException,
    InvocationTargetException, SecurityException, NoSuchMethodException {

  Preconditions.checkArgument(toBeDestroyed.isDirect(),
      "toBeDestroyed isn't direct!");

  Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner");
  cleanerMethod.setAccessible(true);
  Object cleaner = cleanerMethod.invoke(toBeDestroyed);
  Method cleanMethod = cleaner.getClass().getMethod("clean");
  cleanMethod.setAccessible(true);
  cleanMethod.invoke(cleaner);

}
于 2011-11-19T02:46:56.460 に答える
14

ByteBufferドキュメントには次のように記載されています。

allocateDirectこのクラスのファクトリ メソッドを呼び出すことにより、ダイレクト バイト バッファを作成できます。このメソッドによって返されるバッファーは、通常、非直接バッファーよりも割り当てと割り当て解除のコストが多少高くなります。ダイレクト バッファーの内容は、通常のガベージ コレクション ヒープの外に存在する可能性があるため、アプリケーションのメモリ フットプリントへの影響は明らかではありません。したがって、ダイレクト バッファは、主に、基盤となるシステムのネイティブ I/O 操作の対象となる、大きくて存続期間の長いバッファに割り当てることをお勧めします。一般に、ダイレクト バッファを割り当てるのは、プログラムのパフォーマンスが測定可能な程度に向上する場合にのみ行うのが最善です。

特に、「通常のガベージコレクションされたヒープの外にある可能性がある」というステートメントは、あなたの例に関連しているようです。

于 2009-12-06T05:04:10.887 に答える
5

割り当てられたメモリは、ネイティブライブラリを介して実現されます。このメモリは、ByteBuffer#finalizeメソッドが呼び出されたときに解放され、バッファがgcされたときに解放されます。DirectByteBufferImplのallocate()およびfinalize()実装を見てください。

buff.clear()は必要ありません。System.gc()すでに述べたように、ByteBufferオブジェクトへの参照が残っていない場合にのみ役立ちます。

于 2009-12-06T08:37:58.130 に答える
1

Sun (Oracle) 固有の実装に依存している限り、java.nio.DirectByteBuffer の可視性を変更しようとするよりも、反射を介して sun.nio.ch.DirectBuffer インターフェイスを使用することをお勧めします。

/**
 * Sun specific mechanisms to clean up resources associated with direct byte buffers.
 */
@SuppressWarnings("unchecked")
private static final Class<? extends ByteBuffer> SUN_DIRECT_BUFFER = (Class<? extends ByteBuffer>) lookupClassQuietly("sun.nio.ch.DirectBuffer");

private static final Method SUN_BUFFER_CLEANER;

private static final Method SUN_CLEANER_CLEAN;

static
{
    Method bufferCleaner = null;
    Method cleanerClean = null;
    try
    {
        // operate under the assumption that if the sun direct buffer class exists,
        // all of the sun classes exist
        if (SUN_DIRECT_BUFFER != null)
        {
            bufferCleaner = SUN_DIRECT_BUFFER.getMethod("cleaner", (Class[]) null);
            Class<?> cleanClazz = lookupClassQuietly("sun.misc.Cleaner");
            cleanerClean = cleanClazz.getMethod("clean", (Class[]) null);
        }
    }
    catch (Throwable t)
    {
        t.printStackTrace();
    }
    SUN_BUFFER_CLEANER = bufferCleaner;
    SUN_CLEANER_CLEAN = cleanerClean;
}

public static void releaseDirectByteBuffer(ByteBuffer buffer)
{
    if (SUN_DIRECT_BUFFER != null && SUN_DIRECT_BUFFER.isAssignableFrom(buffer.getClass()))
    {
        try
        {
            Object cleaner = SUN_BUFFER_CLEANER.invoke(buffer, (Object[]) null);
            SUN_CLEANER_CLEAN.invoke(cleaner, (Object[]) null);
        }
        catch (Throwable t)
        {
            logger.trace("Exception occurred attempting to clean up Sun specific DirectByteBuffer.", t);
        }
    }
}
于 2014-01-02T16:07:11.643 に答える