If a Buffer object is backed by direct buffer, de-allocate it on close.

This uses the explicit cleaner mechanism attached to direct buffers, and adds
a set of assertions ensuring buffers are not re-used after this call.

There is no unit test for the cleaner call itself, as I can't think of a
straight-forward way to mock a DirectBuffer+ByteBuffer combo (lots of final
things there) - and because that code is merely four lines, it seems straight
forward enough as it is.

This commit should be possible to verify the effects of by using an external
tool that lists what regions of files the neo process has memory mapped while
it is under significant load (enough to cause a lot of PWP refreshes, but not
enough to trigger full GC).
The problem is that since reading of the `limit` is not synchronised in any way there is no publishing guarantees, even if we do synchronise this method. And there is no ordering guarantees when reading limit either, so just because we set limit before we invoke the cleaner here, it doesn't mean that another thread will see limit set to 0 before it tries to access data at the address that we've deallocated with the cleaner.