diff --git a/src/main/java/com/metamx/common/ByteBufferUtils.java b/src/main/java/com/metamx/common/ByteBufferUtils.java index f332d89c..e81275c8 100644 --- a/src/main/java/com/metamx/common/ByteBufferUtils.java +++ b/src/main/java/com/metamx/common/ByteBufferUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2011,2012 Metamarkets Group Inc. + * Copyright 2011-2016 Metamarkets Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,9 @@ package com.metamx.common; -import com.google.common.base.Throwables; +import sun.misc.Cleaner; +import sun.nio.ch.DirectBuffer; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; @@ -27,46 +26,15 @@ */ public class ByteBufferUtils { - private static final Method unmap; - - private static final Method getCleaner; - private static final Method clean; - - - static { - try { - Method unmapMethod = Class.forName("sun.nio.ch.FileChannelImpl") - .getDeclaredMethod("unmap", MappedByteBuffer.class); - unmapMethod.setAccessible(true); - unmap = unmapMethod; - } - catch (Exception e) { - throw new UOE(e, "Exception thrown while trying to find unmap method on MappedByteBuffer, " - + "this method must exist in your VM in order for this to work"); - } - } - - static { - try { - getCleaner = Class.forName("java.nio.DirectByteBuffer").getDeclaredMethod("cleaner"); - getCleaner.setAccessible(true); - clean = Class.forName("sun.misc.Cleaner").getDeclaredMethod("clean"); - clean.setAccessible(true); - } catch(ClassNotFoundException | NoSuchMethodException e) { - throw new UOE("Exception thrown while trying to access ByteBuffer clean method."); - } - } - /** * Releases memory held by the given direct ByteBuffer * * @param buffer buffer to free */ - public static void free(ByteBuffer buffer) { - try { - clean.invoke(getCleaner.invoke(buffer)); - } catch(IllegalAccessException | InvocationTargetException e) { - throw Throwables.propagate(e); + public static void free(ByteBuffer buffer) + { + if (buffer.isDirect()) { + clean((DirectBuffer) buffer); } } @@ -78,11 +46,14 @@ public static void free(ByteBuffer buffer) { */ public static void unmap(MappedByteBuffer buffer) { - try { - unmap.invoke(null, buffer); - } - catch (Exception e) { - throw Throwables.propagate(e); + free(buffer); + } + + private static void clean(DirectBuffer buffer) + { + final Cleaner cleaner = buffer.cleaner(); + if (cleaner != null) { + cleaner.clean(); } } } diff --git a/src/test/java/com/metamx/common/ByteBufferUtilsTest.java b/src/test/java/com/metamx/common/ByteBufferUtilsTest.java new file mode 100644 index 00000000..d83025ea --- /dev/null +++ b/src/test/java/com/metamx/common/ByteBufferUtilsTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2016 Metamarkets Group Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.metamx.common; + +import com.google.common.io.Files; +import junit.framework.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.util.Arrays; + +public class ByteBufferUtilsTest +{ + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void testUnmapDoesntCrashJVM() throws Exception + { + final File file = temporaryFolder.newFile("some_mmap_file"); + try (final OutputStream os = new BufferedOutputStream(new FileOutputStream(file))) { + final byte[] data = new byte[4096]; + Arrays.fill(data, (byte) 0x5A); + os.write(data); + } + final MappedByteBuffer mappedByteBuffer = Files.map(file); + Assert.assertEquals((byte) 0x5A, mappedByteBuffer.get(0)); + ByteBufferUtils.unmap(mappedByteBuffer); + ByteBufferUtils.unmap(mappedByteBuffer); + } + + @Test + public void testFreeDoesntCrashJVM() throws Exception + { + final ByteBuffer directBuffer = ByteBuffer.allocateDirect(4096); + ByteBufferUtils.free(directBuffer); + ByteBufferUtils.free(directBuffer); + + final ByteBuffer heapBuffer = ByteBuffer.allocate(4096); + ByteBufferUtils.free(heapBuffer); + } +}