From f19f21169ef024a722a7c52ba547bf015120e8c9 Mon Sep 17 00:00:00 2001 From: Issac Date: Thu, 5 Feb 2026 16:40:48 +0100 Subject: [PATCH 1/5] fix: does not break class loading if direct buffer allocator is not available --- .../main/java/org/apache/arrow/memory/util/MemoryUtil.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java b/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java index 91bd7cd905..7024e2e343 100644 --- a/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java +++ b/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java @@ -18,6 +18,7 @@ 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.nio.ByteOrder; @@ -109,6 +110,9 @@ public Object run() { } catch (SecurityException e) { logger.debug("Cannot get constructor for direct buffer allocation", e); return e; + } catch (InaccessibleObjectException e) { + logger.debug("Cannot get constructor for direct buffer allocation", e); + return e; } } }); From bec873732c3a734307e1222d50f710efafa259c7 Mon Sep 17 00:00:00 2001 From: Issac Date: Thu, 5 Feb 2026 17:25:11 +0100 Subject: [PATCH 2/5] fix: catch direct buffer addess failure --- .../apache/arrow/memory/util/MemoryUtil.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java b/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java index 7024e2e343..44e6d06a98 100644 --- a/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java +++ b/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java @@ -82,9 +82,17 @@ public Object run() { BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); // get the offset of the address field in a java.nio.Buffer object + long maybeOffset; Field addressField = java.nio.Buffer.class.getDeclaredField("address"); - addressField.setAccessible(true); - BYTE_BUFFER_ADDRESS_OFFSET = UNSAFE.objectFieldOffset(addressField); + try { + addressField.setAccessible(true); + maybeOffset = UNSAFE.objectFieldOffset(addressField); + } catch (InaccessibleObjectException e){ + maybeOffset = -1; + logger.debug("Cannot access the address field of java.nio.Buffer. DirectBuffer operations wont be available", e); + } + BYTE_BUFFER_ADDRESS_OFFSET = maybeOffset; + Constructor directBufferConstructor; long address = -1; @@ -160,7 +168,11 @@ public Object run() { * @return address of the underlying memory. */ public static long getByteBufferAddress(ByteBuffer buf) { - return UNSAFE.getLong(buf, BYTE_BUFFER_ADDRESS_OFFSET); + if(BYTE_BUFFER_ADDRESS_OFFSET != -1) { + return UNSAFE.getLong(buf, BYTE_BUFFER_ADDRESS_OFFSET); + } + throw new UnsupportedOperationException( + "Byte buffer address cannot be obtained because sun.misc.Unsafe or java.nio.DirectByteBuffer.(long, int) is not available"); } private MemoryUtil() {} From 9ddc31973233ce0bfdf289d4b748310628f44d8e Mon Sep 17 00:00:00 2001 From: Issac Date: Thu, 5 Feb 2026 17:52:32 +0100 Subject: [PATCH 3/5] test: Change test to validate we cannot access DirectBuffer --- .../org/apache/arrow/memory/util/MemoryUtil.java | 9 +++++---- .../test/java/org/apache/arrow/memory/TestOpens.java | 12 +++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java b/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java index 44e6d06a98..be0749a215 100644 --- a/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java +++ b/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java @@ -87,13 +87,14 @@ public Object run() { try { addressField.setAccessible(true); maybeOffset = UNSAFE.objectFieldOffset(addressField); - } catch (InaccessibleObjectException e){ + } catch (InaccessibleObjectException e) { maybeOffset = -1; - logger.debug("Cannot access the address field of java.nio.Buffer. DirectBuffer operations wont be available", e); + logger.debug( + "Cannot access the address field of java.nio.Buffer. DirectBuffer operations wont be available", + e); } BYTE_BUFFER_ADDRESS_OFFSET = maybeOffset; - Constructor directBufferConstructor; long address = -1; final ByteBuffer direct = ByteBuffer.allocateDirect(1); @@ -168,7 +169,7 @@ public Object run() { * @return address of the underlying memory. */ public static long getByteBufferAddress(ByteBuffer buf) { - if(BYTE_BUFFER_ADDRESS_OFFSET != -1) { + if (BYTE_BUFFER_ADDRESS_OFFSET != -1) { return UNSAFE.getLong(buf, BYTE_BUFFER_ADDRESS_OFFSET); } throw new UnsupportedOperationException( diff --git a/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java b/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java index b5e0a71e7e..73764c5ea4 100644 --- a/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java +++ b/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java @@ -20,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.condition.JRE.JAVA_16; +import org.apache.arrow.memory.util.MemoryUtil; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledForJreRange; @@ -34,18 +35,15 @@ public void testMemoryUtilFailsLoudly() { Throwable e = assertThrows( Throwable.class, - () -> { - BufferAllocator allocator = new RootAllocator(); - allocator.close(); - }); + () -> MemoryUtil.directBuffer(0, 10)); boolean found = false; while (e != null) { - e = e.getCause(); - if (e instanceof RuntimeException - && e.getMessage().contains("Failed to initialize MemoryUtil")) { + if (e instanceof UnsupportedOperationException + && e.getMessage().contains("java.nio.DirectByteBuffer.(long, int) not available")) { found = true; break; } + e = e.getCause(); } assertTrue(found, "Expected exception was not thrown"); } From 03c764801462f9ee17edf9e85ebf7e2249a0e9a8 Mon Sep 17 00:00:00 2001 From: Issac Date: Thu, 5 Feb 2026 18:19:12 +0100 Subject: [PATCH 4/5] doc: improve test doc --- .../src/test/java/org/apache/arrow/memory/TestOpens.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java b/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java index 73764c5ea4..45d37d1c2e 100644 --- a/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java +++ b/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java @@ -25,7 +25,7 @@ import org.junit.jupiter.api.condition.EnabledForJreRange; public class TestOpens { - /** Instantiating the RootAllocator should poke MemoryUtil and fail. */ + /** Accessing MemoryUtil.directBuffer should fail as add-opens is not configured */ @Test @EnabledForJreRange(min = JAVA_16) public void testMemoryUtilFailsLoudly() { From 115e46525a7eb0287d47e3a92b91568ebb29a0ea Mon Sep 17 00:00:00 2001 From: Issac Date: Fri, 6 Feb 2026 09:31:44 +0100 Subject: [PATCH 5/5] spotless: fix in test --- .../src/test/java/org/apache/arrow/memory/TestOpens.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java b/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java index 45d37d1c2e..f74bf63f82 100644 --- a/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java +++ b/memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java @@ -25,17 +25,14 @@ import org.junit.jupiter.api.condition.EnabledForJreRange; public class TestOpens { - /** Accessing MemoryUtil.directBuffer should fail as add-opens is not configured */ + /** Accessing MemoryUtil.directBuffer should fail as add-opens is not configured. */ @Test @EnabledForJreRange(min = JAVA_16) public void testMemoryUtilFailsLoudly() { // This test is configured by Maven to run WITHOUT add-opens. So this should fail on JDK16+ // (where JEP396 means that add-opens is required to access JDK internals). // The test will likely fail in your IDE if it doesn't correctly pick this up. - Throwable e = - assertThrows( - Throwable.class, - () -> MemoryUtil.directBuffer(0, 10)); + Throwable e = assertThrows(Throwable.class, () -> MemoryUtil.directBuffer(0, 10)); boolean found = false; while (e != null) { if (e instanceof UnsupportedOperationException