(long, int) not available");
- }
-
- @SuppressWarnings(
- "nullness:argument") // to handle null assignment on third party dependency: Unsafe
- private static void copyMemory(
- @Nullable Object srcBase,
- long srcOffset,
- @Nullable Object destBase,
- long destOffset,
- long bytes) {
- UNSAFE.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes);
+ return ACCESSOR.directBuffer(address, capacity);
}
public static void copyMemory(long srcAddress, long destAddress, long bytes) {
- UNSAFE.copyMemory(srcAddress, destAddress, bytes);
+ ACCESSOR.copyMemory(srcAddress, destAddress, bytes);
}
public static void copyToMemory(byte[] src, long srcIndex, long destAddress, long bytes) {
- copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, destAddress, bytes);
+ ACCESSOR.copyToMemory(src, srcIndex, destAddress, bytes);
}
public static void copyFromMemory(long srcAddress, byte[] dest, long destIndex, long bytes) {
- copyMemory(null, srcAddress, dest, BYTE_ARRAY_BASE_OFFSET + destIndex, bytes);
+ ACCESSOR.copyFromMemory(srcAddress, dest, destIndex, bytes);
}
public static byte getByte(long address) {
- return UNSAFE.getByte(address);
+ return ACCESSOR.getByte(address);
}
public static void putByte(long address, byte value) {
- UNSAFE.putByte(address, value);
+ ACCESSOR.putByte(address, value);
}
public static short getShort(long address) {
- return UNSAFE.getShort(address);
+ return ACCESSOR.getShort(address);
}
public static void putShort(long address, short value) {
- UNSAFE.putShort(address, value);
+ ACCESSOR.putShort(address, value);
}
public static int getInt(long address) {
- return UNSAFE.getInt(address);
+ return ACCESSOR.getInt(address);
}
public static void putInt(long address, int value) {
- UNSAFE.putInt(address, value);
+ ACCESSOR.putInt(address, value);
}
public static long getLong(long address) {
- return UNSAFE.getLong(address);
+ return ACCESSOR.getLong(address);
}
public static void putLong(long address, long value) {
- UNSAFE.putLong(address, value);
+ ACCESSOR.putLong(address, value);
}
public static void setMemory(long address, long bytes, byte value) {
- UNSAFE.setMemory(address, bytes, value);
+ ACCESSOR.setMemory(address, bytes, value);
}
public static int getInt(byte[] bytes, int index) {
- return UNSAFE.getInt(bytes, BYTE_ARRAY_BASE_OFFSET + index);
+ return ACCESSOR.getInt(bytes, index);
}
public static long getLong(byte[] bytes, int index) {
- return UNSAFE.getLong(bytes, BYTE_ARRAY_BASE_OFFSET + index);
+ return ACCESSOR.getLong(bytes, index);
}
public static long allocateMemory(long bytes) {
- return UNSAFE.allocateMemory(bytes);
+ return ACCESSOR.allocateMemory(bytes);
}
+ /**
+ * Frees native memory at the given address.
+ *
+ * Behavior depends on the accessor selected by {@value #MEMORY_ACCESSOR_TYPE_PROPERTY_NAME}.
+ * The default Unsafe accessor frees any valid native address, whatever allocated it. The FFM
+ * accessor ({@code org.apache.arrow.memory.ffm.FfmMemoryAccessor}) only frees addresses that came
+ * from its own {@link #allocateMemory}, because it releases the owning {@code Arena} rather than
+ * the address; an address from any other source (JNI, a foreign {@code malloc}) is a silent no-op
+ * there.
+ */
public static void freeMemory(long address) {
- UNSAFE.freeMemory(address);
+ ACCESSOR.freeMemory(address);
}
}
diff --git a/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtilAccessor.java b/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtilAccessor.java
new file mode 100644
index 0000000000..10acddbc52
--- /dev/null
+++ b/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtilAccessor.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.arrow.memory.util;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Pluggable backend for {@link MemoryUtil}'s low-level memory operations. The default
+ * implementation ({@link UnsafeMemoryAccessor}) is backed by {@code sun.misc.Unsafe}; the {@code
+ * arrow-memory-ffm} module supplies an alternative backed by {@code java.lang.foreign}.
+ */
+public interface MemoryUtilAccessor {
+ long allocateMemory(long bytes);
+
+ void freeMemory(long address);
+
+ byte getByte(long address);
+
+ void putByte(long address, byte value);
+
+ short getShort(long address);
+
+ void putShort(long address, short value);
+
+ int getInt(long address);
+
+ void putInt(long address, int value);
+
+ long getLong(long address);
+
+ void putLong(long address, long value);
+
+ void setMemory(long address, long bytes, byte value);
+
+ void copyMemory(long srcAddress, long destAddress, long bytes);
+
+ void copyToMemory(byte[] src, long srcIndex, long destAddress, long bytes);
+
+ void copyFromMemory(long srcAddress, byte[] dest, long destIndex, long bytes);
+
+ int getInt(byte[] bytes, int index);
+
+ long getLong(byte[] bytes, int index);
+
+ long getByteBufferAddress(ByteBuffer buf);
+
+ ByteBuffer directBuffer(long address, int capacity);
+}
diff --git a/memory/memory-core/src/main/java/org/apache/arrow/memory/util/UnsafeMemoryAccessor.java b/memory/memory-core/src/main/java/org/apache/arrow/memory/util/UnsafeMemoryAccessor.java
new file mode 100644
index 0000000000..08474f2f46
--- /dev/null
+++ b/memory/memory-core/src/main/java/org/apache/arrow/memory/util/UnsafeMemoryAccessor.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.arrow.memory.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InaccessibleObjectException;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import sun.misc.Unsafe;
+
+/** {@link MemoryUtilAccessor} backed by {@code sun.misc.Unsafe}. The default accessor. */
+final class UnsafeMemoryAccessor implements MemoryUtilAccessor {
+ private static final org.slf4j.Logger logger =
+ org.slf4j.LoggerFactory.getLogger(UnsafeMemoryAccessor.class);
+
+ static final MemoryUtilAccessor INSTANCE = new UnsafeMemoryAccessor();
+
+ private static final @Nullable Constructor> DIRECT_BUFFER_CONSTRUCTOR;
+ private static final Unsafe UNSAFE;
+ private static final long BYTE_ARRAY_BASE_OFFSET;
+ private static final long BYTE_BUFFER_ADDRESS_OFFSET;
+
+ // Java 1.8, 9, 11, 17, 21 becomes 1, 9, 11, 17, and 21.
+ @SuppressWarnings("StringSplitter")
+ private static final int majorVersion =
+ Integer.parseInt(System.getProperty("java.specification.version").split("\\D+")[0]);
+
+ static {
+ try {
+ // try to get the unsafe object
+ final Object maybeUnsafe =
+ AccessController.doPrivileged(
+ new PrivilegedAction