Merge pull request #20616 from akka/wip-17194-DirectByteBuffer-cleaner-patriknw

Attempt cleaning of DirectByteBuffer, #17194
This commit is contained in:
Patrik Nordwall 2016-06-02 17:42:10 +02:00
commit cae070bd93

View file

@ -5,6 +5,7 @@
package akka.io package akka.io
import java.nio.ByteBuffer import java.nio.ByteBuffer
import scala.util.control.NonFatal
trait BufferPool { trait BufferPool {
def acquire(): ByteBuffer def acquire(): ByteBuffer
@ -54,11 +55,42 @@ private[akka] class DirectByteBufferPool(defaultBufferSize: Int, maxPoolEntries:
} }
} }
private final def offerBufferToPool(buf: ByteBuffer): Unit = private final def offerBufferToPool(buf: ByteBuffer): Unit = {
pool.synchronized { val clean =
if (buffersInPool < maxPoolEntries) { pool.synchronized {
pool(buffersInPool) = buf if (buffersInPool < maxPoolEntries) {
buffersInPool += 1 pool(buffersInPool) = buf
} // else let the buffer be gc'd buffersInPool += 1
false
} else {
// try to clean it outside the lock, or let the buffer be gc'd
true
}
}
if (clean)
tryCleanDirectByteBuffer(buf)
}
/**
* 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.
*
* Utilizes reflection to avoid dependency to `sun.misc.Cleaner`.
*/
private final def tryCleanDirectByteBuffer(toBeDestroyed: ByteBuffer): Unit = try {
if (toBeDestroyed.isDirect) {
val cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner")
cleanerMethod.setAccessible(true)
val cleaner = cleanerMethod.invoke(toBeDestroyed)
val cleanMethod = cleaner.getClass().getMethod("clean")
cleanMethod.setAccessible(true)
cleanMethod.invoke(cleaner)
} }
} catch {
case NonFatal(_) // attempt failed, ok
}
} }