2011-07-14 111 views
8

我明白,当一个直接的缓冲区被分配,它不受垃圾回收,但我想知道的是,如果包装对象是垃圾回收。例如,如果我分配了一个新的DirectByteBuffer dbb,然后使用dbb.duplicate()复制(浅拷贝)它,我会在同一块内存中创建两个包装。是否收集Java DirectByteBuffer包装垃圾?

这些包装是否被垃圾收集?如果我没有

while(true){ 
    DirectByteBuffer dbb2 = dbb.duplicate(); 
} 

我最终会OOM自己吗?

+0

其实,一个DirectByteBuffer和它的本地内存_might_可能被垃圾收集。它使用PhantomReference来释放本机分配的内存。 –

回答

10

在Sun JDK,一个java.nio.DirectByteBuffer —通过ByteBuffer#allocateDirect(int) —创建具有sun.misc.Cleaner型,其延伸java.lang.ref.PhantomReference的场。

当此Cleaner(记住,的PhantomReference亚型)被收集并即将进入相关ReferenceQueue,通过嵌套式ReferenceHandler运行收集相关的线程有Cleaner情况下的一种特殊情况处理:它向下转换和致电Cleaner#clean(),最终返回DirectByteBuffer$Deallocator#run(),然后调用Unsafe#freeMemory(long)。哇。

这很迂回,我很惊讶没有看到Object#finalize()在使用中的任何用途。 Sun开发人员必须有理由将其与收集和参考管理子系统更紧密地联系起来。

简而言之,只要垃圾收集器有机会注意到放弃并且它的引用处理线程通过上述调用取得了进展,您将不会由于放弃对DirectByteBuffer实例的引用而耗尽内存。

+2

请注意,如果您使用'-XX:+ DisableExplicitGC',那么您可能会遇到'allocateDirect'更多的'OutOfMemory'错误。分配会尝试保留内存,如果不能触发'PhantomReference'的回收,则调用'System.gc()'。 –

1

查看源代码到DirectByteBuffer它只是返回一个新的实例,所以不,你不会自己OOM。

只要你的代码的其余部分不能保留对原始dbb的引用,那么该对象将正常收到垃圾回收。额外的dbb2对象同样会在不再有任何引用(即while循环的结尾)时收集垃圾。

2

直接ByteBuffer对象就像任何其他对象一样:它可以被垃圾收集。

ByteBuffer对象为GC'd(ByteBuffer未明确声明,但由MappedByteBuffer的文档暗示)时,直接缓冲区使用的内存将被释放。

事情变得有趣的是当你用虚拟内存空间填充了直接缓冲区,但仍然有很多空间。事实证明(至少在Sun JVM上),在分配直接缓冲区时用完虚拟空间将触发Java堆的GC。其中可能收集未引用的直接缓冲区并释放其虚拟内存承诺。

如果您在64位机器上运行,您应该使用-XX:MaxDirectMemorySize,这会给您可以分配的缓冲区数量设置一个上限(并且在您达到该限制时触发GC)。

+0

如何使用DirectByteBuffers帮助垃圾收集暂停,ala TerraCotta的BigMemory? –

+0

@李丕:因为你可以在一个直接缓冲区中存储很多序列化的Java对象。 – parsifal

-2

当directbytebuffer分配,它不属于垃圾收集

你在哪里会有这种想法?这是不正确的。你是否将它们与MappedByteBuffers混合?

+0

@downvoter为什么? – EJP