This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new ccc79e9c69 GH-37286: [Java] Start adding nullability/nullness 
annotations (#37723)
ccc79e9c69 is described below

commit ccc79e9c6946501806cfb2a6b6ea86d4d0097df1
Author: david dali susanibar arce <[email protected]>
AuthorDate: Tue Jan 9 09:18:55 2024 -0500

    GH-37286: [Java] Start adding nullability/nullness annotations (#37723)
    
    ### Rationale for this change
    
    Closes: https://github.com/apache/arrow/issues/37286
    
    ### What changes are included in this PR?
    
    Initial support for:
    
    - Use the Checker Framework to enhances Java’s type system to make it more 
powerful and useful. Planning to start with [Nullness 
Checker](https://checkerframework.org/manual/#nullness-checker)
    
    ### Are these changes tested?
    
    These are the activities involved on this PR:
    
    - [x] Configure the Checker Framework
    - [x] Treat checker errors as warnings initially
    - [x] Applying Nullness Checker annotation as needed: @ NonNull / @ Nullable
    - [x] Check if building timer increases after this checker is added
    - [x] Fixes for code review
    
    ### Are there any user-facing changes?
    
    Yes
    * Closes: #37286
    
    Lead-authored-by: david dali susanibar arce <[email protected]>
    Co-authored-by: David Susanibar Arce <[email protected]>
    Co-authored-by: David Susanibar Arce <[email protected]>
    Signed-off-by: David Li <[email protected]>
---
 java/memory/memory-core/pom.xml                    |  45 +++++
 .../java/org/apache/arrow/memory/Accountant.java   |  14 +-
 .../org/apache/arrow/memory/AllocationManager.java |  12 +-
 .../org/apache/arrow/memory/AllocationOutcome.java |   7 +-
 .../arrow/memory/AllocationOutcomeDetails.java     |   5 +-
 .../java/org/apache/arrow/memory/ArrowBuf.java     |  39 ++---
 .../org/apache/arrow/memory/BaseAllocator.java     | 189 +++++++++++++--------
 .../org/apache/arrow/memory/BufferAllocator.java   |   3 +-
 .../java/org/apache/arrow/memory/BufferLedger.java | 106 ++++++------
 .../memory/DefaultAllocationManagerOption.java     |   9 +-
 .../arrow/memory/LowCostIdentityHashMap.java       |  37 ++--
 .../apache/arrow/memory/util/ArrowBufPointer.java  |   9 +-
 .../apache/arrow/memory/util/HistoricalLog.java    |  13 +-
 .../org/apache/arrow/memory/util/MemoryUtil.java   |  15 +-
 .../org/apache/arrow/memory/util/StackTrace.java   |  27 +--
 .../arrow/memory/util/hash/MurmurHasher.java       |   3 +-
 .../arrow/memory/util/hash/SimpleHasher.java       |   3 +-
 .../java/org/apache/arrow/util/Preconditions.java  |   6 +
 java/pom.xml                                       |   7 +
 19 files changed, 357 insertions(+), 192 deletions(-)

diff --git a/java/memory/memory-core/pom.xml b/java/memory/memory-core/pom.xml
index 8f28699045..b914b1fa10 100644
--- a/java/memory/memory-core/pom.xml
+++ b/java/memory/memory-core/pom.xml
@@ -35,6 +35,10 @@
       <groupId>org.immutables</groupId>
       <artifactId>value</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.checkerframework</groupId>
+      <artifactId>checker-qual</artifactId>
+    </dependency>
   </dependencies>
 
   <build>
@@ -90,5 +94,46 @@
         </plugins>
       </build>
     </profile>
+
+    <profile>
+      <id>checkerframework-jdk11+</id>
+      <activation>
+        <jdk>[11,]</jdk>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-compiler-plugin</artifactId>
+            <configuration>
+              <source>8</source>
+              <target>8</target>
+              <encoding>UTF-8</encoding>
+              <compilerArgs combine.children="append">
+                <arg>-Xmaxerrs</arg> <!-- javac only reports the first 100 
errors or warnings -->
+                <arg>10000</arg>
+                <arg>-Xmaxwarns</arg>
+                <arg>10000</arg>
+                <arg>-AskipDefs=.*Test</arg> <!-- Skip analysis for Testing 
classes -->
+                <arg>-AatfDoNotCache</arg> <!-- not cache results -->
+              </compilerArgs>
+              <annotationProcessorPaths combine.children="append">
+                <path>
+                  <groupId>org.checkerframework</groupId>
+                  <artifactId>checker</artifactId>
+                  <version>${checker.framework.version}</version>
+                </path>
+              </annotationProcessorPaths>
+              <annotationProcessors>
+                <!-- To support @Value.Immutable processors -->
+                
<annotationProcessor>org.immutables.value.internal.$processor$.$Processor</annotationProcessor>
+                <!-- Add all the checkers you want to enable here -->
+                
<annotationProcessor>org.checkerframework.checker.nullness.NullnessChecker</annotationProcessor>
+              </annotationProcessors>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
   </profiles>
 </project>
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/Accountant.java 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/Accountant.java
index 87769dd122..b87f1345a5 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/Accountant.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/Accountant.java
@@ -22,6 +22,7 @@ import java.util.concurrent.atomic.AtomicLong;
 import javax.annotation.concurrent.ThreadSafe;
 
 import org.apache.arrow.util.Preconditions;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * Provides a concurrent way to manage account for memory usage without 
locking. Used as basis
@@ -34,7 +35,7 @@ class Accountant implements AutoCloseable {
   /**
    * The parent allocator.
    */
-  protected final Accountant parent;
+  protected final @Nullable Accountant parent;
 
   private final String name;
 
@@ -59,7 +60,7 @@ class Accountant implements AutoCloseable {
    */
   private final AtomicLong locallyHeldMemory = new AtomicLong();
 
-  public Accountant(Accountant parent, String name, long reservation, long 
maxAllocation) {
+  public Accountant(@Nullable Accountant parent, String name, long 
reservation, long maxAllocation) {
     Preconditions.checkNotNull(name, "name must not be null");
     Preconditions.checkArgument(reservation >= 0, "The initial reservation 
size must be non-negative.");
     Preconditions.checkArgument(maxAllocation >= 0, "The maximum allocation 
limit must be non-negative.");
@@ -73,12 +74,13 @@ class Accountant implements AutoCloseable {
     this.allocationLimit.set(maxAllocation);
 
     if (reservation != 0) {
+      Preconditions.checkArgument(parent != null, "parent must not be null");
       // we will allocate a reservation from our parent.
       final AllocationOutcome outcome = parent.allocateBytes(reservation);
       if (!outcome.isOk()) {
         throw new OutOfMemoryException(String.format(
-            "Failure trying to allocate initial reservation for Allocator. " +
-                "Attempted to allocate %d bytes.", reservation), 
outcome.getDetails());
+                "Failure trying to allocate initial reservation for Allocator. 
" +
+                        "Attempted to allocate %d bytes.", reservation), 
outcome.getDetails());
       }
     }
   }
@@ -103,7 +105,7 @@ class Accountant implements AutoCloseable {
     }
   }
 
-  private AllocationOutcome.Status allocateBytesInternal(long size, 
AllocationOutcomeDetails details) {
+  private AllocationOutcome.Status allocateBytesInternal(long size, @Nullable 
AllocationOutcomeDetails details) {
     final AllocationOutcome.Status status = allocate(size,
         true /*incomingUpdatePeek*/, false /*forceAllocation*/, details);
     if (!status.isOk()) {
@@ -168,7 +170,7 @@ class Accountant implements AutoCloseable {
    * @return The outcome of the allocation.
    */
   private AllocationOutcome.Status allocate(final long size, final boolean 
incomingUpdatePeak,
-      final boolean forceAllocation, AllocationOutcomeDetails details) {
+      final boolean forceAllocation, @Nullable AllocationOutcomeDetails 
details) {
     final long oldLocal = locallyHeldMemory.getAndAdd(size);
     final long newLocal = oldLocal + size;
     // Borrowed from Math.addExact (but avoid exception here)
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationManager.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationManager.java
index 3071c02f30..6ccefdd9c1 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationManager.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationManager.java
@@ -18,6 +18,7 @@
 package org.apache.arrow.memory;
 
 import org.apache.arrow.util.Preconditions;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * An AllocationManager is the implementation of a physical memory allocation.
@@ -48,8 +49,9 @@ public abstract class AllocationManager {
   // This is mostly a semantic constraint on the API user: if the reference 
count reaches 0 in the owningLedger, then
   // there are not supposed to be any references through other allocators. In 
practice, this doesn't do anything
   // as the implementation just forces ownership to be transferred to one of 
the other extant references.
-  private volatile BufferLedger owningLedger;
+  private volatile @Nullable BufferLedger owningLedger;
 
+  @SuppressWarnings("nullness:method.invocation") //call to associate(a, b) 
not allowed on the given receiver
   protected AllocationManager(BufferAllocator accountingAllocator) {
     Preconditions.checkNotNull(accountingAllocator);
     accountingAllocator.assertOpen();
@@ -61,7 +63,7 @@ public abstract class AllocationManager {
     this.owningLedger = associate(accountingAllocator, false);
   }
 
-  BufferLedger getOwningLedger() {
+  @Nullable BufferLedger getOwningLedger() {
     return owningLedger;
   }
 
@@ -133,9 +135,9 @@ public abstract class AllocationManager {
     // remove the <BaseAllocator, BufferLedger> mapping for the allocator
     // of calling BufferLedger
     Preconditions.checkState(map.containsKey(allocator),
-        "Expecting a mapping for allocator and reference manager");
+            "Expecting a mapping for allocator and reference manager");
     final BufferLedger oldLedger = map.remove(allocator);
-
+    Preconditions.checkState(oldLedger != null, "Expecting a mapping for 
allocator and reference manager");
     BufferAllocator oldAllocator = oldLedger.getAllocator();
     if (oldAllocator instanceof BaseAllocator) {
       // needed for debug only: tell the allocator that AllocationManager is 
removing a
@@ -168,7 +170,7 @@ public abstract class AllocationManager {
       // the release call was made by a non-owning reference manager, so after 
remove there have
       // to be 1 or more <allocator, reference manager> mappings
       Preconditions.checkState(map.size() > 0,
-          "The final removal of reference manager should be connected to 
owning reference manager");
+              "The final removal of reference manager should be connected to 
owning reference manager");
     }
   }
 
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationOutcome.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationOutcome.java
index 2977775e6c..21a57eee49 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationOutcome.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationOutcome.java
@@ -19,15 +19,18 @@ package org.apache.arrow.memory;
 
 import java.util.Optional;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+
 /**
  * Describes the type of outcome that occurred when trying to account for 
allocation of memory.
  */
 public class AllocationOutcome {
   private final Status status;
-  private final AllocationOutcomeDetails details;
+  private final @Nullable AllocationOutcomeDetails details;
   static final AllocationOutcome SUCCESS_INSTANCE = new 
AllocationOutcome(Status.SUCCESS);
 
-  AllocationOutcome(Status status, AllocationOutcomeDetails details) {
+  AllocationOutcome(Status status, @Nullable AllocationOutcomeDetails details) 
{
     this.status = status;
     this.details = details;
   }
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationOutcomeDetails.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationOutcomeDetails.java
index 6499ce84b1..3ceda71cce 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationOutcomeDetails.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/AllocationOutcomeDetails.java
@@ -20,6 +20,9 @@ package org.apache.arrow.memory;
 import java.util.ArrayDeque;
 import java.util.Deque;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+
 /**
  * Captures details of allocation for each accountant in the hierarchical 
chain.
  */
@@ -47,7 +50,7 @@ public class AllocationOutcomeDetails {
    * Get the allocator that caused the failure.
    * @return the allocator that caused failure, null if there was no failure.
    */
-  public BufferAllocator getFailedAllocator() {
+  public @Nullable BufferAllocator getFailedAllocator() {
     Entry top = allocEntries.peekLast();
     if (top != null && top.allocationFailed && (top.accountant instanceof 
BufferAllocator)) {
       return (BufferAllocator) top.accountant;
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/ArrowBuf.java 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/ArrowBuf.java
index 2c2e93b2d7..112d36ece0 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/ArrowBuf.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/ArrowBuf.java
@@ -33,6 +33,7 @@ import org.apache.arrow.memory.util.HistoricalLog;
 import org.apache.arrow.memory.util.MemoryUtil;
 import org.apache.arrow.util.Preconditions;
 import org.apache.arrow.util.VisibleForTesting;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * ArrowBuf serves as a facade over underlying memory by providing
@@ -68,11 +69,11 @@ public final class ArrowBuf implements AutoCloseable {
   private static final int LOG_BYTES_PER_ROW = 10;
   private final long id = idGenerator.incrementAndGet();
   private final ReferenceManager referenceManager;
-  private final BufferManager bufferManager;
+  private final @Nullable BufferManager bufferManager;
   private final long addr;
   private long readerIndex;
   private long writerIndex;
-  private final HistoricalLog historicalLog = BaseAllocator.DEBUG ?
+  private final @Nullable HistoricalLog historicalLog = BaseAllocator.DEBUG ?
           new HistoricalLog(BaseAllocator.DEBUG_LOG_LENGTH, "ArrowBuf[%d]", 
id) : null;
   private volatile long capacity;
 
@@ -84,7 +85,7 @@ public final class ArrowBuf implements AutoCloseable {
    */
   public ArrowBuf(
       final ReferenceManager referenceManager,
-      final BufferManager bufferManager,
+      final @Nullable BufferManager bufferManager,
       final long capacity,
       final long memoryAddress) {
     this.referenceManager = referenceManager;
@@ -93,7 +94,7 @@ public final class ArrowBuf implements AutoCloseable {
     this.capacity = capacity;
     this.readerIndex = 0;
     this.writerIndex = 0;
-    if (BaseAllocator.DEBUG) {
+    if (historicalLog != null) {
       historicalLog.recordEvent("create()");
     }
   }
@@ -244,7 +245,7 @@ public final class ArrowBuf implements AutoCloseable {
   }
 
   @Override
-  public boolean equals(Object obj) {
+  public boolean equals(@Nullable Object obj) {
     // identity equals only.
     return this == obj;
   }
@@ -313,7 +314,7 @@ public final class ArrowBuf implements AutoCloseable {
     // check bounds
     Preconditions.checkArgument(fieldLength >= 0, "expecting non-negative data 
length");
     if (index < 0 || index > capacity() - fieldLength) {
-      if (BaseAllocator.DEBUG) {
+      if (historicalLog != null) {
         historicalLog.logHistory(logger);
       }
       throw new IndexOutOfBoundsException(String.format(
@@ -736,7 +737,7 @@ public final class ArrowBuf implements AutoCloseable {
     if (length != 0) {
       // copy "length" bytes from this ArrowBuf starting at addr(index) address
       // into dst byte array at dstIndex onwards
-      MemoryUtil.UNSAFE.copyMemory(null, addr(index), dst, 
MemoryUtil.BYTE_ARRAY_BASE_OFFSET + dstIndex, length);
+      MemoryUtil.copyMemory(null, addr(index), dst, 
MemoryUtil.BYTE_ARRAY_BASE_OFFSET + dstIndex, length);
     }
   }
 
@@ -773,7 +774,7 @@ public final class ArrowBuf implements AutoCloseable {
     if (length > 0) {
       // copy "length" bytes from src byte array at the starting index 
(srcIndex)
       // into this ArrowBuf starting at address "addr(index)"
-      MemoryUtil.UNSAFE.copyMemory(src, MemoryUtil.BYTE_ARRAY_BASE_OFFSET + 
srcIndex, null, addr(index), length);
+      MemoryUtil.copyMemory(src, MemoryUtil.BYTE_ARRAY_BASE_OFFSET + srcIndex, 
null, addr(index), length);
     }
   }
 
@@ -799,7 +800,7 @@ public final class ArrowBuf implements AutoCloseable {
         // at address srcAddress into the dst ByteBuffer starting at
         // address dstAddress
         final long dstAddress = MemoryUtil.getByteBufferAddress(dst) + 
dst.position();
-        MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, 
dst.remaining());
+        MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, 
dst.remaining());
         // after copy, bump the next write position for the dst ByteBuffer
         dst.position(dst.position() + dst.remaining());
       } else if (dst.hasArray()) {
@@ -807,7 +808,7 @@ public final class ArrowBuf implements AutoCloseable {
         // at address srcAddress into the dst ByteBuffer starting at
         // index dstIndex
         final int dstIndex = dst.arrayOffset() + dst.position();
-        MemoryUtil.UNSAFE.copyMemory(
+        MemoryUtil.copyMemory(
                 null, srcAddress, dst.array(), 
MemoryUtil.BYTE_ARRAY_BASE_OFFSET + dstIndex, dst.remaining());
         // after copy, bump the next write position for the dst ByteBuffer
         dst.position(dst.position() + dst.remaining());
@@ -836,14 +837,14 @@ public final class ArrowBuf implements AutoCloseable {
         // copy src.remaining() bytes of data from src ByteBuffer starting at
         // address srcAddress into this ArrowBuf starting at address dstAddress
         final long srcAddress = MemoryUtil.getByteBufferAddress(src) + 
src.position();
-        MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, 
length);
+        MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, length);
         // after copy, bump the next read position for the src ByteBuffer
         src.position(src.position() + length);
       } else if (src.hasArray()) {
         // copy src.remaining() bytes of data from src ByteBuffer starting at
         // index srcIndex into this ArrowBuf starting at address dstAddress
         final int srcIndex = src.arrayOffset() + src.position();
-        MemoryUtil.UNSAFE.copyMemory(
+        MemoryUtil.copyMemory(
                 src.array(), MemoryUtil.BYTE_ARRAY_BASE_OFFSET + srcIndex, 
null, dstAddress, length);
         // after copy, bump the next read position for the src ByteBuffer
         src.position(src.position() + length);
@@ -896,7 +897,7 @@ public final class ArrowBuf implements AutoCloseable {
       // srcAddress into this ArrowBuf at address dstAddress
       final long srcAddress = MemoryUtil.getByteBufferAddress(src) + srcIndex;
       final long dstAddress = addr(index);
-      MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, length);
+      MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, length);
     } else {
       if (srcIndex == 0 && src.capacity() == length) {
         // copy the entire ByteBuffer from start to end of length
@@ -936,7 +937,7 @@ public final class ArrowBuf implements AutoCloseable {
       // dstAddress
       final long srcAddress = addr(index);
       final long dstAddress = dst.memoryAddress() + (long) dstIndex;
-      MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, length);
+      MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, length);
     }
   }
 
@@ -966,7 +967,7 @@ public final class ArrowBuf implements AutoCloseable {
       // dstAddress
       final long srcAddress = src.memoryAddress() + srcIndex;
       final long dstAddress = addr(index);
-      MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, length);
+      MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, length);
     }
   }
 
@@ -986,7 +987,7 @@ public final class ArrowBuf implements AutoCloseable {
     checkIndex(index, length);
     final long srcAddress = src.memoryAddress() + src.readerIndex;
     final long dstAddress = addr(index);
-    MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, length);
+    MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, length);
     src.readerIndex(src.readerIndex + length);
   }
 
@@ -1011,7 +1012,7 @@ public final class ArrowBuf implements AutoCloseable {
       if (readBytes > 0) {
         // copy readBytes length of data from the tmp byte array starting
         // at srcIndex 0 into this ArrowBuf starting at address addr(index)
-        MemoryUtil.UNSAFE.copyMemory(tmp, MemoryUtil.BYTE_ARRAY_BASE_OFFSET, 
null, addr(index), readBytes);
+        MemoryUtil.copyMemory(tmp, MemoryUtil.BYTE_ARRAY_BASE_OFFSET, null, 
addr(index), readBytes);
       }
     }
     return readBytes;
@@ -1033,7 +1034,7 @@ public final class ArrowBuf implements AutoCloseable {
       // copy length bytes of data from this ArrowBuf starting at
       // address addr(index) into the tmp byte array starting at index 0
       byte[] tmp = new byte[length];
-      MemoryUtil.UNSAFE.copyMemory(null, addr(index), tmp, 
MemoryUtil.BYTE_ARRAY_BASE_OFFSET, length);
+      MemoryUtil.copyMemory(null, addr(index), tmp, 
MemoryUtil.BYTE_ARRAY_BASE_OFFSET, length);
       // write the copied data to output stream
       out.write(tmp);
     }
@@ -1109,7 +1110,7 @@ public final class ArrowBuf implements AutoCloseable {
   public void print(StringBuilder sb, int indent, Verbosity verbosity) {
     CommonUtil.indent(sb, indent).append(toString());
 
-    if (BaseAllocator.DEBUG && verbosity.includeHistoricalLog) {
+    if (historicalLog != null && verbosity.includeHistoricalLog) {
       sb.append("\n");
       historicalLog.buildHistory(sb, indent + 1, verbosity.includeStackTraces);
     }
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BaseAllocator.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BaseAllocator.java
index 9337f48b74..8779c7a343 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BaseAllocator.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BaseAllocator.java
@@ -30,6 +30,10 @@ import org.apache.arrow.memory.util.AssertionUtil;
 import org.apache.arrow.memory.util.CommonUtil;
 import org.apache.arrow.memory.util.HistoricalLog;
 import org.apache.arrow.util.Preconditions;
+import org.checkerframework.checker.initialization.qual.Initialized;
+import org.checkerframework.checker.nullness.qual.KeyFor;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
 import org.immutables.value.Value;
 
 /**
@@ -64,17 +68,17 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
   // Package exposed for sharing between AllocatorManger and BaseAllocator 
objects
   private final String name;
   private final RootAllocator root;
-  private final Object DEBUG_LOCK = DEBUG ? new Object() : null;
+  private final Object DEBUG_LOCK = new Object();
   private final AllocationListener listener;
-  private final BaseAllocator parentAllocator;
+  private final @Nullable BaseAllocator parentAllocator;
   private final Map<BaseAllocator, Object> childAllocators;
   private final ArrowBuf empty;
   // members used purely for debugging
-  private final IdentityHashMap<BufferLedger, Object> childLedgers;
-  private final IdentityHashMap<Reservation, Object> reservations;
-  private final HistoricalLog historicalLog;
+  private final @Nullable IdentityHashMap<BufferLedger, @Nullable Object> 
childLedgers;
+  private final @Nullable IdentityHashMap<Reservation, Object> reservations;
+  private final @Nullable HistoricalLog historicalLog;
   private final RoundingPolicy roundingPolicy;
-  private final AllocationManager.Factory allocationManagerFactory;
+  private final AllocationManager.@NonNull Factory allocationManagerFactory;
 
   private volatile boolean isClosed = false; // the allocator has been closed
 
@@ -87,8 +91,10 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
    *
    * @see Config
    */
+  @SuppressWarnings({"nullness:method.invocation", "nullness:cast.unsafe"})
+  //{"call to hist(,...) not allowed on the given receiver.", "cast cannot be 
statically verified"}
   protected BaseAllocator(
-      final BaseAllocator parentAllocator,
+      final @Nullable BaseAllocator parentAllocator,
       final String name,
       final Config config) throws OutOfMemoryException {
     super(parentAllocator, name, config.getInitReservation(), 
config.getMaxAllocation());
@@ -100,7 +106,7 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
       this.root = parentAllocator.root;
       empty = parentAllocator.empty;
     } else if (this instanceof RootAllocator) {
-      this.root = (RootAllocator) this;
+      this.root = (@Initialized RootAllocator) this;
       empty = createEmpty();
     } else {
       throw new IllegalStateException("An parent allocator must either carry a 
root or be the " +
@@ -131,7 +137,7 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
   }
 
   @Override
-  public BaseAllocator getParentAllocator() {
+  public @Nullable BaseAllocator getParentAllocator() {
     return parentAllocator;
   }
 
@@ -187,7 +193,9 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
     assertOpen();
     if (DEBUG) {
       synchronized (DEBUG_LOCK) {
-        childLedgers.put(ledger, null);
+        if (childLedgers != null) {
+          childLedgers.put(ledger, null);
+        }
       }
     }
   }
@@ -201,6 +209,7 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
     assertOpen();
     if (DEBUG) {
       synchronized (DEBUG_LOCK) {
+        Preconditions.checkState(childLedgers != null, "childLedgers must not 
be null");
         if (!childLedgers.containsKey(ledger)) {
           throw new IllegalStateException("Trying to remove a child ledger 
that doesn't exist.");
         }
@@ -223,7 +232,9 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
       synchronized (DEBUG_LOCK) {
         final Object object = childAllocators.remove(childAllocator);
         if (object == null) {
-          childAllocator.historicalLog.logHistory(logger);
+          if (childAllocator.historicalLog != null) {
+            childAllocator.historicalLog.logHistory(logger);
+          }
           throw new IllegalStateException("Child allocator[" + 
childAllocator.name +
             "] not found in parent allocator[" + name + "]'s childAllocators");
         }
@@ -280,12 +291,13 @@ abstract class BaseAllocator extends Accountant 
implements BufferAllocator {
     return buffer(initialRequestSize, null);
   }
 
+  @SuppressWarnings("nullness:dereference.of.nullable")//dereference of 
possibly-null reference allocationManagerFactory
   private ArrowBuf createEmpty() {
     return allocationManagerFactory.empty();
   }
 
   @Override
-  public ArrowBuf buffer(final long initialRequestSize, BufferManager manager) 
{
+  public ArrowBuf buffer(final long initialRequestSize, @Nullable 
BufferManager manager) {
     assertOpen();
 
     Preconditions.checkArgument(initialRequestSize >= 0, "the requested size 
must be non-negative");
@@ -332,7 +344,7 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
    */
   private ArrowBuf bufferWithoutReservation(
       final long size,
-      BufferManager bufferManager) throws OutOfMemoryException {
+      @Nullable BufferManager bufferManager) throws OutOfMemoryException {
     assertOpen();
 
     final AllocationManager manager = newAllocationManager(size);
@@ -388,8 +400,10 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
     if (DEBUG) {
       synchronized (DEBUG_LOCK) {
         childAllocators.put(childAllocator, childAllocator);
-        historicalLog.recordEvent("allocator[%s] created new child 
allocator[%s]", name,
-            childAllocator.getName());
+        if (historicalLog != null) {
+          historicalLog.recordEvent("allocator[%s] created new child 
allocator[%s]", name,
+              childAllocator.getName());
+        }
       }
     } else {
       childAllocators.put(childAllocator, childAllocator);
@@ -439,14 +453,14 @@ abstract class BaseAllocator extends Accountant 
implements BufferAllocator {
         }
 
         // are there outstanding buffers?
-        final int allocatedCount = childLedgers.size();
+        final int allocatedCount = childLedgers != null ? childLedgers.size() 
: 0;
         if (allocatedCount > 0) {
           throw new IllegalStateException(
             String.format("Allocator[%s] closed with outstanding buffers 
allocated (%d).\n%s",
               name, allocatedCount, toString()));
         }
 
-        if (reservations.size() != 0) {
+        if (reservations != null && reservations.size() != 0) {
           throw new IllegalStateException(
             String.format("Allocator[%s] closed with outstanding reservations 
(%d).\n%s", name,
               reservations.size(),
@@ -486,7 +500,9 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
     }
 
     if (DEBUG) {
-      historicalLog.recordEvent("closed");
+      if (historicalLog != null) {
+        historicalLog.recordEvent("closed");
+      }
       logger.debug(String.format("closed allocator[%s].", name));
     }
 
@@ -517,7 +533,9 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
   }
 
   private void hist(String noteFormat, Object... args) {
-    historicalLog.recordEvent(noteFormat, args);
+    if (historicalLog != null) {
+      historicalLog.recordEvent(noteFormat, args);
+    }
   }
 
   /**
@@ -567,10 +585,14 @@ abstract class BaseAllocator extends Accountant 
implements BufferAllocator {
         childTotal += Math.max(childAllocator.getAllocatedMemory(), 
childAllocator.reservation);
       }
       if (childTotal > getAllocatedMemory()) {
-        historicalLog.logHistory(logger);
+        if (historicalLog != null) {
+          historicalLog.logHistory(logger);
+        }
         logger.debug("allocator[" + name + "] child event logs BEGIN");
         for (final BaseAllocator childAllocator : childSet) {
-          childAllocator.historicalLog.logHistory(logger);
+          if (childAllocator.historicalLog != null) {
+            childAllocator.historicalLog.logHistory(logger);
+          }
         }
         logger.debug("allocator[" + name + "] child event logs END");
         throw new IllegalStateException(
@@ -581,33 +603,39 @@ abstract class BaseAllocator extends Accountant 
implements BufferAllocator {
       // Furthermore, the amount I've allocated should be that plus buffers 
I've allocated.
       long bufferTotal = 0;
 
-      final Set<BufferLedger> ledgerSet = childLedgers.keySet();
-      for (final BufferLedger ledger : ledgerSet) {
-        if (!ledger.isOwningLedger()) {
-          continue;
-        }
+      final Set<@KeyFor("this.childLedgers") BufferLedger> ledgerSet = 
childLedgers != null ?
+              childLedgers.keySet() : null;
+      if (ledgerSet != null) {
+        for (final BufferLedger ledger : ledgerSet) {
+          if (!ledger.isOwningLedger()) {
+            continue;
+          }
 
-        final AllocationManager am = ledger.getAllocationManager();
-        /*
-         * Even when shared, ArrowBufs are rewrapped, so we should never see 
the same instance
-         * twice.
-         */
-        final BaseAllocator otherOwner = buffersSeen.get(am);
-        if (otherOwner != null) {
-          throw new IllegalStateException("This allocator's ArrowBuf already 
owned by another " +
-            "allocator");
-        }
-        buffersSeen.put(am, this);
+          final AllocationManager am = ledger.getAllocationManager();
+          /*
+           * Even when shared, ArrowBufs are rewrapped, so we should never see 
the same instance
+           * twice.
+           */
+          final BaseAllocator otherOwner = buffersSeen.get(am);
+          if (otherOwner != null) {
+            throw new IllegalStateException("This allocator's ArrowBuf already 
owned by another " +
+              "allocator");
+          }
+          buffersSeen.put(am, this);
 
-        bufferTotal += am.getSize();
+          bufferTotal += am.getSize();
+        }
       }
 
       // Preallocated space has to be accounted for
-      final Set<Reservation> reservationSet = reservations.keySet();
+      final Set<@KeyFor("this.reservations") Reservation> reservationSet = 
reservations != null ?
+              reservations.keySet() : null;
       long reservedTotal = 0;
-      for (final Reservation reservation : reservationSet) {
-        if (!reservation.isUsed()) {
-          reservedTotal += reservation.getSize();
+      if (reservationSet != null) {
+        for (final Reservation reservation : reservationSet) {
+          if (!reservation.isUsed()) {
+            reservedTotal += reservation.getSize();
+          }
         }
       }
 
@@ -644,9 +672,13 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
 
         if (reservedTotal != 0) {
           sb.append(String.format("reserved total : %d bytes.", 
reservedTotal));
-          for (final Reservation reservation : reservationSet) {
-            reservation.historicalLog.buildHistory(sb, 0, true);
-            sb.append('\n');
+          if (reservationSet != null) {
+            for (final Reservation reservation : reservationSet) {
+              if (reservation.historicalLog != null) {
+                reservation.historicalLog.buildHistory(sb, 0, true);
+              }
+              sb.append('\n');
+            }
           }
         }
 
@@ -689,16 +721,25 @@ abstract class BaseAllocator extends Accountant 
implements BufferAllocator {
         child.print(sb, level + 2, verbosity);
       }
 
-      CommonUtil.indent(sb, level + 1).append(String.format("ledgers: %d\n", 
childLedgers.size()));
-      for (BufferLedger ledger : childLedgers.keySet()) {
-        ledger.print(sb, level + 2, verbosity);
+      CommonUtil.indent(sb, level + 1).append(String.format("ledgers: %d\n", 
childLedgers != null ?
+              childLedgers.size() : 0));
+      if (childLedgers != null) {
+        for (BufferLedger ledger : childLedgers.keySet()) {
+          ledger.print(sb, level + 2, verbosity);
+        }
       }
 
-      final Set<Reservation> reservations = this.reservations.keySet();
-      CommonUtil.indent(sb, level + 1).append(String.format("reservations: 
%d\n", reservations.size()));
-      for (final Reservation reservation : reservations) {
-        if (verbosity.includeHistoricalLog) {
-          reservation.historicalLog.buildHistory(sb, level + 3, true);
+      final Set<@KeyFor("this.reservations") Reservation> reservations = 
this.reservations != null ?
+              this.reservations.keySet() : null;
+      CommonUtil.indent(sb, level + 1).append(String.format("reservations: 
%d\n",
+              reservations != null ? reservations.size() : 0));
+      if (reservations != null) {
+        for (final Reservation reservation : reservations) {
+          if (verbosity.includeHistoricalLog) {
+            if (reservation.historicalLog != null) {
+              reservation.historicalLog.buildHistory(sb, level + 3, true);
+            }
+          }
         }
       }
 
@@ -706,17 +747,20 @@ abstract class BaseAllocator extends Accountant 
implements BufferAllocator {
 
   }
 
-  private void dumpBuffers(final StringBuilder sb, final Set<BufferLedger> 
ledgerSet) {
-    for (final BufferLedger ledger : ledgerSet) {
-      if (!ledger.isOwningLedger()) {
-        continue;
+  private void dumpBuffers(final StringBuilder sb,
+                           final @Nullable Set<@KeyFor("this.childLedgers") 
BufferLedger> ledgerSet) {
+    if (ledgerSet != null) {
+      for (final BufferLedger ledger : ledgerSet) {
+        if (!ledger.isOwningLedger()) {
+          continue;
+        }
+        final AllocationManager am = ledger.getAllocationManager();
+        sb.append("UnsafeDirectLittleEndian[identityHashCode == ");
+        sb.append(Integer.toString(System.identityHashCode(am)));
+        sb.append("] size ");
+        sb.append(Long.toString(am.getSize()));
+        sb.append('\n');
       }
-      final AllocationManager am = ledger.getAllocationManager();
-      sb.append("UnsafeDirectLittleEndian[identityHashCode == ");
-      sb.append(Integer.toString(System.identityHashCode(am)));
-      sb.append("] size ");
-      sb.append(Long.toString(am.getSize()));
-      sb.append('\n');
     }
   }
 
@@ -813,7 +857,7 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
    */
   public class Reservation implements AllocationReservation {
 
-    private final HistoricalLog historicalLog;
+    private final @Nullable HistoricalLog historicalLog;
     private int nBytes = 0;
     private boolean used = false;
     private boolean closed = false;
@@ -824,13 +868,16 @@ abstract class BaseAllocator extends Accountant 
implements BufferAllocator {
      * <p>If {@linkplain #DEBUG} is true this will capture a historical
      * log of events relevant to this Reservation.
      */
+    @SuppressWarnings("nullness:argument")//to handle null assignment on third 
party dependency: System.identityHashCode
     public Reservation() {
       if (DEBUG) {
         historicalLog = new HistoricalLog("Reservation[allocator[%s], %d]", 
name, System
           .identityHashCode(this));
         historicalLog.recordEvent("created");
         synchronized (DEBUG_LOCK) {
-          reservations.put(this, this);
+          if (reservations != null) {
+            reservations.put(this, this);
+          }
         }
       } else {
         historicalLog = null;
@@ -901,7 +948,7 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
         if (!isClosed()) {
           final Object object;
           synchronized (DEBUG_LOCK) {
-            object = reservations.remove(this);
+            object = reservations != null ? reservations.remove(this) : null;
           }
           if (object == null) {
             final StringBuilder sb = new StringBuilder();
@@ -911,7 +958,9 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
               System.identityHashCode(this)));
           }
 
-          historicalLog.recordEvent("closed");
+          if (historicalLog != null) {
+            historicalLog.recordEvent("closed");
+          }
         }
       }
 
@@ -928,7 +977,7 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
 
       final AllocationOutcome outcome = 
BaseAllocator.this.allocateBytes(nBytes);
 
-      if (DEBUG) {
+      if (historicalLog != null) {
         historicalLog.recordEvent("reserve(%d) => %s", nBytes, 
Boolean.toString(outcome.isOk()));
       }
 
@@ -959,7 +1008,7 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
         final ArrowBuf arrowBuf = 
BaseAllocator.this.bufferWithoutReservation(nBytes, null);
 
         listener.onAllocation(nBytes);
-        if (DEBUG) {
+        if (historicalLog != null) {
           historicalLog.recordEvent("allocate() => %s", 
String.format("ArrowBuf[%d]", arrowBuf
               .getId()));
         }
@@ -982,7 +1031,7 @@ abstract class BaseAllocator extends Accountant implements 
BufferAllocator {
 
       releaseBytes(nBytes);
 
-      if (DEBUG) {
+      if (historicalLog != null) {
         historicalLog.recordEvent("releaseReservation(%d)", nBytes);
       }
     }
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BufferAllocator.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BufferAllocator.java
index 90a4ef26fb..c279e18f1e 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BufferAllocator.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BufferAllocator.java
@@ -21,6 +21,7 @@ import java.util.Collection;
 
 import org.apache.arrow.memory.rounding.DefaultRoundingPolicy;
 import org.apache.arrow.memory.rounding.RoundingPolicy;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * Wrapper class to deal with byte buffer allocation. Ensures users only use 
designated methods.
@@ -166,7 +167,7 @@ public interface BufferAllocator extends AutoCloseable {
    *
    * @return parent allocator
    */
-  BufferAllocator getParentAllocator();
+  @Nullable BufferAllocator getParentAllocator();
 
   /**
    * Returns the set of child allocators.
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BufferLedger.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BufferLedger.java
index 48b3e183d5..1ca3e08ecf 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BufferLedger.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/BufferLedger.java
@@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicLong;
 import org.apache.arrow.memory.util.CommonUtil;
 import org.apache.arrow.memory.util.HistoricalLog;
 import org.apache.arrow.util.Preconditions;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * The reference manager that binds an {@link AllocationManager} to
@@ -32,7 +33,7 @@ import org.apache.arrow.util.Preconditions;
  * fate (same reference count).
  */
 public class BufferLedger implements ValueWithKeyIncluded<BufferAllocator>, 
ReferenceManager {
-  private final IdentityHashMap<ArrowBuf, Object> buffers =
+  private final @Nullable IdentityHashMap<ArrowBuf, @Nullable Object> buffers =
           BaseAllocator.DEBUG ? new IdentityHashMap<>() : null;
   private static final AtomicLong LEDGER_ID_GENERATOR = new AtomicLong(0);
   // unique ID assigned to each ledger
@@ -43,7 +44,7 @@ public class BufferLedger implements 
ValueWithKeyIncluded<BufferAllocator>, Refe
   private final long lCreationTime = System.nanoTime();
   private final BufferAllocator allocator;
   private final AllocationManager allocationManager;
-  private final HistoricalLog historicalLog =
+  private final @Nullable HistoricalLog historicalLog =
       BaseAllocator.DEBUG ? new HistoricalLog(BaseAllocator.DEBUG_LOG_LENGTH,
         "BufferLedger[%d]", 1) : null;
   private volatile long lDestructionTime = 0;
@@ -122,7 +123,7 @@ public class BufferLedger implements 
ValueWithKeyIncluded<BufferAllocator>, Refe
           "ref count decrement should be greater than or equal to 1");
     // decrement the ref count
     final int refCnt = decrement(decrement);
-    if (BaseAllocator.DEBUG) {
+    if (historicalLog != null) {
       historicalLog.recordEvent("release(%d). original value: %d",
           decrement, refCnt + decrement);
     }
@@ -178,7 +179,7 @@ public class BufferLedger implements 
ValueWithKeyIncluded<BufferAllocator>, Refe
   @Override
   public void retain(int increment) {
     Preconditions.checkArgument(increment > 0, "retain(%s) argument is not 
positive", increment);
-    if (BaseAllocator.DEBUG) {
+    if (historicalLog != null) {
       historicalLog.recordEvent("retain(%d)", increment);
     }
     final int originalReferenceCount = bufRefCnt.getAndAdd(increment);
@@ -233,20 +234,7 @@ public class BufferLedger implements 
ValueWithKeyIncluded<BufferAllocator>, Refe
             );
 
     // logging
-    if (BaseAllocator.DEBUG) {
-      historicalLog.recordEvent(
-              "ArrowBuf(BufferLedger, BufferAllocator[%s], " +
-                      "UnsafeDirectLittleEndian[identityHashCode == " +
-                      "%d](%s)) => ledger hc == %d",
-              allocator.getName(), System.identityHashCode(derivedBuf), 
derivedBuf.toString(),
-              System.identityHashCode(this));
-
-      synchronized (buffers) {
-        buffers.put(derivedBuf, null);
-      }
-    }
-
-    return derivedBuf;
+    return loggingArrowBufHistoricalLog(derivedBuf);
   }
 
   /**
@@ -261,7 +249,7 @@ public class BufferLedger implements 
ValueWithKeyIncluded<BufferAllocator>, Refe
    * @return A new ArrowBuf that shares references with all ArrowBufs 
associated
    *         with this BufferLedger
    */
-  ArrowBuf newArrowBuf(final long length, final BufferManager manager) {
+  ArrowBuf newArrowBuf(final long length, final @Nullable BufferManager 
manager) {
     allocator.assertOpen();
 
     // the start virtual address of the ArrowBuf will be same as address of 
memory chunk
@@ -271,13 +259,17 @@ public class BufferLedger implements 
ValueWithKeyIncluded<BufferAllocator>, Refe
     final ArrowBuf buf = new ArrowBuf(this, manager, length, startAddress);
 
     // logging
-    if (BaseAllocator.DEBUG) {
+    return loggingArrowBufHistoricalLog(buf);
+  }
+
+  private ArrowBuf loggingArrowBufHistoricalLog(ArrowBuf buf) {
+    if (historicalLog != null) {
       historicalLog.recordEvent(
           "ArrowBuf(BufferLedger, BufferAllocator[%s], " +
           "UnsafeDirectLittleEndian[identityHashCode == " + "%d](%s)) => 
ledger hc == %d",
           allocator.getName(), System.identityHashCode(buf), buf.toString(),
           System.identityHashCode(this));
-
+      Preconditions.checkState(buffers != null, "IdentityHashMap of buffers 
must not be null");
       synchronized (buffers) {
         buffers.put(buf, null);
       }
@@ -306,7 +298,7 @@ public class BufferLedger implements 
ValueWithKeyIncluded<BufferAllocator>, Refe
   @Override
   public ArrowBuf retain(final ArrowBuf srcBuffer, BufferAllocator target) {
 
-    if (BaseAllocator.DEBUG) {
+    if (historicalLog != null) {
       historicalLog.recordEvent("retain(%s)", target.getName());
     }
 
@@ -333,45 +325,48 @@ public class BufferLedger implements 
ValueWithKeyIncluded<BufferAllocator>, Refe
    * @param targetReferenceManager The ledger to transfer ownership account to.
    * @return Whether transfer fit within target ledgers limits.
    */
-  boolean transferBalance(final ReferenceManager targetReferenceManager) {
+  boolean transferBalance(final @Nullable ReferenceManager 
targetReferenceManager) {
     Preconditions.checkArgument(targetReferenceManager != null,
-        "Expecting valid target reference manager");
-    final BufferAllocator targetAllocator = 
targetReferenceManager.getAllocator();
-    Preconditions.checkArgument(allocator.getRoot() == 
targetAllocator.getRoot(),
-        "You can only transfer between two allocators that share the same 
root.");
-
-    allocator.assertOpen();
-    targetReferenceManager.getAllocator().assertOpen();
-
-    // if we're transferring to ourself, just return.
-    if (targetReferenceManager == this) {
-      return true;
-    }
-
-    // since two balance transfers out from the allocation manager could cause 
incorrect
-    // accounting, we need to ensure
-    // that this won't happen by synchronizing on the allocation manager 
instance.
-    synchronized (allocationManager) {
-      if (allocationManager.getOwningLedger() != this) {
-        // since the calling reference manager is not the owning
-        // reference manager for the underlying memory, transfer is
-        // a NO-OP
+            "Expecting valid target reference manager");
+    boolean overlimit = false;
+    if (targetReferenceManager != null) {
+      final BufferAllocator targetAllocator = 
targetReferenceManager.getAllocator();
+      Preconditions.checkArgument(allocator.getRoot() == 
targetAllocator.getRoot(),
+              "You can only transfer between two allocators that share the 
same root.");
+
+      allocator.assertOpen();
+      targetReferenceManager.getAllocator().assertOpen();
+
+      // if we're transferring to ourself, just return.
+      if (targetReferenceManager == this) {
         return true;
       }
 
-      if (BaseAllocator.DEBUG) {
-        this.historicalLog.recordEvent("transferBalance(%s)",
-            targetReferenceManager.getAllocator().getName());
-      }
+      // since two balance transfers out from the allocation manager could 
cause incorrect
+      // accounting, we need to ensure
+      // that this won't happen by synchronizing on the allocation manager 
instance.
+      synchronized (allocationManager) {
+        if (allocationManager.getOwningLedger() != this) {
+          // since the calling reference manager is not the owning
+          // reference manager for the underlying memory, transfer is
+          // a NO-OP
+          return true;
+        }
 
-      boolean overlimit = 
targetAllocator.forceAllocate(allocationManager.getSize());
-      allocator.releaseBytes(allocationManager.getSize());
-      // since the transfer can only happen from the owning reference manager,
-      // we need to set the target ref manager as the new owning ref manager
-      // for the chunk of memory in allocation manager
-      allocationManager.setOwningLedger((BufferLedger) targetReferenceManager);
-      return overlimit;
+        if (BaseAllocator.DEBUG && this.historicalLog != null) {
+          this.historicalLog.recordEvent("transferBalance(%s)",
+                  targetReferenceManager.getAllocator().getName());
+        }
+
+        overlimit = targetAllocator.forceAllocate(allocationManager.getSize());
+        allocator.releaseBytes(allocationManager.getSize());
+        // since the transfer can only happen from the owning reference 
manager,
+        // we need to set the target ref manager as the new owning ref manager
+        // for the chunk of memory in allocation manager
+        allocationManager.setOwningLedger((BufferLedger) 
targetReferenceManager);
+      }
     }
+    return overlimit;
   }
 
   /**
@@ -501,6 +496,7 @@ public class BufferLedger implements 
ValueWithKeyIncluded<BufferAllocator>, Refe
     if (!BaseAllocator.DEBUG) {
       sb.append("]\n");
     } else {
+      Preconditions.checkArgument(buffers != null, "IdentityHashMap of buffers 
must not be null");
       synchronized (buffers) {
         sb.append("] holds ")
           .append(buffers.size())
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/DefaultAllocationManagerOption.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/DefaultAllocationManagerOption.java
index 15120c252f..d57b72ba41 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/DefaultAllocationManagerOption.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/DefaultAllocationManagerOption.java
@@ -19,6 +19,9 @@ package org.apache.arrow.memory;
 
 import java.lang.reflect.Field;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+
 /**
  * A class for choosing the default allocation manager.
  */
@@ -39,7 +42,7 @@ public class DefaultAllocationManagerOption {
   /**
    * The default allocation manager factory.
    */
-  private static AllocationManager.Factory DEFAULT_ALLOCATION_MANAGER_FACTORY 
= null;
+  private static AllocationManager.@Nullable Factory 
DEFAULT_ALLOCATION_MANAGER_FACTORY = null;
 
   /**
    * The allocation manager type.
@@ -61,6 +64,7 @@ public class DefaultAllocationManagerOption {
     Unknown,
   }
 
+  @SuppressWarnings("nullness:argument") //enum types valueOf are implicitly 
non-null
   static AllocationManagerType getDefaultAllocationManagerType() {
     AllocationManagerType ret = AllocationManagerType.Unknown;
 
@@ -103,6 +107,9 @@ public class DefaultAllocationManagerOption {
     return DEFAULT_ALLOCATION_MANAGER_FACTORY;
   }
 
+  @SuppressWarnings({"nullness:argument", "nullness:return"})
+  //incompatible argument for parameter obj of Field.get
+  // Static member qualifying type may not be annotated
   private static AllocationManager.Factory getFactory(String clazzName) {
     try {
       Field field = Class.forName(clazzName).getDeclaredField("FACTORY");
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/LowCostIdentityHashMap.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/LowCostIdentityHashMap.java
index edfa82392a..740233ef41 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/LowCostIdentityHashMap.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/LowCostIdentityHashMap.java
@@ -19,6 +19,9 @@ package org.apache.arrow.memory;
 
 import org.apache.arrow.util.Preconditions;
 import org.apache.arrow.util.VisibleForTesting;
+import org.checkerframework.checker.initialization.qual.Initialized;
+import org.checkerframework.checker.initialization.qual.UnderInitialization;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * Highly specialized IdentityHashMap that implements only partial
@@ -35,7 +38,7 @@ public class LowCostIdentityHashMap<K, V extends 
ValueWithKeyIncluded<K>> {
   /*
    * The internal data structure to hold values.
    */
-  private Object[] elementData;
+  private @Nullable Object [] elementData; // elementData[index] = null;
 
   /* Actual number of values. */
   private int size;
@@ -69,19 +72,20 @@ public class LowCostIdentityHashMap<K, V extends 
ValueWithKeyIncluded<K>> {
     if (maxSize >= 0) {
       this.size = 0;
       threshold = getThreshold(maxSize);
-      elementData = newElementArray(computeElementArraySize());
+      elementData = newElementArrayUnderInitialized(computeElementArraySize());
     } else {
       throw new IllegalArgumentException();
     }
   }
 
-  private int getThreshold(int maxSize) {
+  private int getThreshold(@UnderInitialization LowCostIdentityHashMap<K, V> 
this,
+                           int maxSize) {
     // assign the threshold to maxSize initially, this will change to a
     // higher value if rehashing occurs.
     return maxSize > 2 ? maxSize : 2;
   }
 
-  private int computeElementArraySize() {
+  private int computeElementArraySize(@UnderInitialization 
LowCostIdentityHashMap<K, V> this) {
     int arraySize = (int) (((long) threshold * 10000) / LOAD_FACTOR);
     // ensure arraySize is positive, the above cast from long to int type
     // leads to overflow and negative arraySize if threshold is too big
@@ -95,7 +99,18 @@ public class LowCostIdentityHashMap<K, V extends 
ValueWithKeyIncluded<K>> {
    *            the number of elements
    * @return Reference to the element array
    */
-  private Object[] newElementArray(int s) {
+  private Object[] newElementArrayInitialized(@Initialized 
LowCostIdentityHashMap<K, V> this, int s) {
+    return new Object[s];
+  }
+
+  /**
+   * Create a new element array.
+   *
+   * @param s
+   *            the number of elements
+   * @return Reference to the element array
+   */
+  private Object[] newElementArrayUnderInitialized(@UnderInitialization 
LowCostIdentityHashMap<K, V> this, int s) {
     return new Object[s];
   }
 
@@ -152,7 +167,7 @@ public class LowCostIdentityHashMap<K, V extends 
ValueWithKeyIncluded<K>> {
    * @param key the key.
    * @return the value of the mapping with the specified key.
    */
-  public V get(K key) {
+  public @Nullable V get(K key) {
     Preconditions.checkNotNull(key);
 
     int index = findIndex(key, elementData);
@@ -166,7 +181,7 @@ public class LowCostIdentityHashMap<K, V extends 
ValueWithKeyIncluded<K>> {
    * empty spot if the key is not found in this table.
    */
   @VisibleForTesting
-  int findIndex(Object key, Object[] array) {
+  int findIndex(@Nullable Object key, @Nullable Object[] array) {
     int length = array.length;
     int index = getModuloHash(key, length);
     int last = (index + length - 1) % length;
@@ -184,7 +199,7 @@ public class LowCostIdentityHashMap<K, V extends 
ValueWithKeyIncluded<K>> {
   }
 
   @VisibleForTesting
-  static int getModuloHash(Object key, int length) {
+  static int getModuloHash(@Nullable Object key, int length) {
     return ((System.identityHashCode(key) & 0x7FFFFFFF) % length);
   }
 
@@ -226,7 +241,7 @@ public class LowCostIdentityHashMap<K, V extends 
ValueWithKeyIncluded<K>> {
     if (newlength == 0) {
       newlength = 1;
     }
-    Object[] newData = newElementArray(newlength);
+    @Nullable Object[] newData = newElementArrayInitialized(newlength);
     for (int i = 0; i < elementData.length; i++) {
       Object key = (elementData[i] == null) ? null : ((V) 
elementData[i]).getKey();
       if (key != null) {
@@ -250,7 +265,7 @@ public class LowCostIdentityHashMap<K, V extends 
ValueWithKeyIncluded<K>> {
    * @return the value of the removed mapping, or {@code null} if no mapping
    *         for the specified key was found.
    */
-  public V remove(K key) {
+  public @Nullable V remove(K key) {
     Preconditions.checkNotNull(key);
 
     boolean hashedOk;
@@ -325,7 +340,7 @@ public class LowCostIdentityHashMap<K, V extends 
ValueWithKeyIncluded<K>> {
    *
    * @return next available value or null if none available
    */
-  public V getNextValue() {
+  public @Nullable V getNextValue() {
     for (int i = 0; i < elementData.length; i++) {
       if (elementData[i] != null) {
         return (V) elementData[i];
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/ArrowBufPointer.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/ArrowBufPointer.java
index fa1cfbdb29..b41576847d 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/ArrowBufPointer.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/ArrowBufPointer.java
@@ -21,6 +21,7 @@ import org.apache.arrow.memory.ArrowBuf;
 import org.apache.arrow.memory.util.hash.ArrowBufHasher;
 import org.apache.arrow.memory.util.hash.SimpleHasher;
 import org.apache.arrow.util.Preconditions;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * Pointer to a memory region within an {@link ArrowBuf}.
@@ -33,7 +34,7 @@ public final class ArrowBufPointer {
    */
   public static final int NULL_HASH_CODE = 0;
 
-  private ArrowBuf buf;
+  private @Nullable ArrowBuf buf;
 
   private long offset;
 
@@ -62,6 +63,7 @@ public final class ArrowBufPointer {
   public ArrowBufPointer(ArrowBufHasher hasher) {
     Preconditions.checkNotNull(hasher);
     this.hasher = hasher;
+    this.buf = null;
   }
 
   /**
@@ -93,6 +95,7 @@ public final class ArrowBufPointer {
    * @param offset the start off set of the memory region pointed to.
    * @param length the length off set of the memory region pointed to.
    */
+
   public void set(ArrowBuf buf, long offset, long length) {
     this.buf = buf;
     this.offset = offset;
@@ -105,7 +108,7 @@ public final class ArrowBufPointer {
    * Gets the underlying buffer, or null if the underlying data is invalid or 
null.
    * @return the underlying buffer, if any, or null if the underlying data is 
invalid or null.
    */
-  public ArrowBuf getBuf() {
+  public @Nullable ArrowBuf getBuf() {
     return buf;
   }
 
@@ -118,7 +121,7 @@ public final class ArrowBufPointer {
   }
 
   @Override
-  public boolean equals(Object o) {
+  public boolean equals(@Nullable Object o) {
     if (this == o) {
       return true;
     }
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/HistoricalLog.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/HistoricalLog.java
index f02539a8a3..21f063c939 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/HistoricalLog.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/HistoricalLog.java
@@ -20,6 +20,7 @@ package org.apache.arrow.memory.util;
 import java.util.Arrays;
 import java.util.LinkedList;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
 import org.slf4j.Logger;
 
 /**
@@ -32,7 +33,7 @@ public class HistoricalLog {
   private final LinkedList<Event> history = new LinkedList<>();
   private final String idString; // the formatted id string
   private final int limit; // the limit on the number of events kept
-  private Event firstEvent; // the first stack trace recorded
+  private @Nullable Event firstEvent; // the first stack trace recorded
 
   /**
    * Constructor. The format string will be formatted and have its arguments
@@ -68,6 +69,7 @@ public class HistoricalLog {
   public HistoricalLog(final int limit, final String idStringFormat, Object... 
args) {
     this.limit = limit;
     this.idString = String.format(idStringFormat, args);
+    this.firstEvent = null;
   }
 
   /**
@@ -122,13 +124,16 @@ public class HistoricalLog {
         .append('\n');
 
     if (firstEvent != null) {
+      long time = firstEvent.time;
+      String note = firstEvent.note;
+      final StackTrace stackTrace = firstEvent.stackTrace;
       sb.append(innerIndentation)
-          .append(firstEvent.time)
+          .append(time)
           .append(' ')
-          .append(firstEvent.note)
+          .append(note)
           .append('\n');
       if (includeStackTrace) {
-        firstEvent.stackTrace.writeToBuilder(sb, indent + 2);
+        stackTrace.writeToBuilder(sb, indent + 2);
       }
 
       for (final Event event : history) {
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java
index cc615c5b38..f79cf79531 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java
@@ -25,15 +25,18 @@ import java.nio.ByteOrder;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
+
 import sun.misc.Unsafe;
 
+
 /**
  * Utilities for memory related operations.
  */
 public class MemoryUtil {
   private static final org.slf4j.Logger logger = 
org.slf4j.LoggerFactory.getLogger(MemoryUtil.class);
 
-  private static final Constructor<?> DIRECT_BUFFER_CONSTRUCTOR;
+  private static final @Nullable Constructor<?> DIRECT_BUFFER_CONSTRUCTOR;
   /**
    * The unsafe object from which to access the off-heap memory.
    */
@@ -63,6 +66,9 @@ public class MemoryUtil {
       // try to get the unsafe object
       final Object maybeUnsafe = AccessController.doPrivileged(new 
PrivilegedAction<Object>() {
         @Override
+        @SuppressWarnings({"nullness:argument", "nullness:return"})
+        // incompatible argument for parameter obj of Field.get
+        // incompatible types in return
         public Object run() {
           try {
             final Field unsafeField = 
Unsafe.class.getDeclaredField("theUnsafe");
@@ -179,4 +185,11 @@ public class MemoryUtil {
     throw new UnsupportedOperationException(
         "sun.misc.Unsafe or java.nio.DirectByteBuffer.<init>(long, int) not 
available");
   }
+
+  @SuppressWarnings("nullness:argument") //to handle null assignment on third 
party dependency: Unsafe
+  public static void copyMemory(@Nullable Object srcBase, long srcOffset,
+                                @Nullable Object destBase, long destOffset,
+                                long bytes) {
+    UNSAFE.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes);
+  }
 }
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/StackTrace.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/StackTrace.java
index cd864eb998..a533edd793 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/StackTrace.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/StackTrace.java
@@ -19,12 +19,15 @@ package org.apache.arrow.memory.util;
 
 import java.util.Arrays;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+
 /**
  * Convenient way of obtaining and manipulating stack traces for debugging.
  */
 public class StackTrace {
 
-  private final StackTraceElement[] stackTraceElements;
+  private final @Nullable StackTraceElement [] stackTraceElements;
 
   /**
    * Constructor. Captures the current stack trace.
@@ -48,16 +51,18 @@ public class StackTrace {
 
     // write the stack trace in standard Java format
     for (StackTraceElement ste : stackTraceElements) {
-      sb.append(indentation)
-          .append("at ")
-          .append(ste.getClassName())
-          .append('.')
-          .append(ste.getMethodName())
-          .append('(')
-          .append(ste.getFileName())
-          .append(':')
-          .append(Integer.toString(ste.getLineNumber()))
-          .append(")\n");
+      if (ste != null) {
+        sb.append(indentation)
+            .append("at ")
+            .append(ste.getClassName())
+            .append('.')
+            .append(ste.getMethodName())
+            .append('(')
+            .append(ste.getFileName())
+            .append(':')
+            .append(Integer.toString(ste.getLineNumber()))
+            .append(")\n");
+      }
     }
   }
 
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/hash/MurmurHasher.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/hash/MurmurHasher.java
index 75fc3f0c45..5de98d23bb 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/hash/MurmurHasher.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/hash/MurmurHasher.java
@@ -19,6 +19,7 @@ package org.apache.arrow.memory.util.hash;
 
 import org.apache.arrow.memory.ArrowBuf;
 import org.apache.arrow.memory.util.MemoryUtil;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * Implementation of the Murmur hashing algorithm.
@@ -157,7 +158,7 @@ public class MurmurHasher implements ArrowBufHasher {
   }
 
   @Override
-  public boolean equals(Object o) {
+  public boolean equals(@Nullable Object o) {
     if (this == o) {
       return true;
     }
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/hash/SimpleHasher.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/hash/SimpleHasher.java
index da0ee48299..3bf3c2a828 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/hash/SimpleHasher.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/memory/util/hash/SimpleHasher.java
@@ -20,6 +20,7 @@ package org.apache.arrow.memory.util.hash;
 
 import org.apache.arrow.memory.ArrowBuf;
 import org.apache.arrow.memory.util.MemoryUtil;
+import org.checkerframework.checker.nullness.qual.Nullable;
 
 /**
  * A simple hasher that calculates the hash code of integers as is,
@@ -110,7 +111,7 @@ public class SimpleHasher implements ArrowBufHasher {
   }
 
   @Override
-  public boolean equals(Object obj) {
+  public boolean equals(@Nullable Object obj) {
     return obj != null && (obj instanceof SimpleHasher);
   }
 }
diff --git 
a/java/memory/memory-core/src/main/java/org/apache/arrow/util/Preconditions.java
 
b/java/memory/memory-core/src/main/java/org/apache/arrow/util/Preconditions.java
index 0ffc9447e4..8083033007 100644
--- 
a/java/memory/memory-core/src/main/java/org/apache/arrow/util/Preconditions.java
+++ 
b/java/memory/memory-core/src/main/java/org/apache/arrow/util/Preconditions.java
@@ -33,6 +33,8 @@
 
 package org.apache.arrow.util;
 
+import org.checkerframework.dataflow.qual.AssertMethod;
+
 /**
  * Static convenience methods that help a method or constructor check whether 
it was invoked
  * correctly (whether its <i>preconditions</i> have been met). These methods 
generally accept a
@@ -117,6 +119,7 @@ public final class Preconditions {
    * @param expression a boolean expression
    * @throws IllegalArgumentException if {@code expression} is false
    */
+  @AssertMethod
   public static void checkArgument(boolean expression) {
     if (!expression) {
       throw new IllegalArgumentException();
@@ -131,6 +134,7 @@ public final class Preconditions {
    *     string using {@link String#valueOf(Object)}
    * @throws IllegalArgumentException if {@code expression} is false
    */
+  @AssertMethod
   public static void checkArgument(boolean expression, Object errorMessage) {
     if (!expression) {
       throw new IllegalArgumentException(String.valueOf(errorMessage));
@@ -438,6 +442,7 @@ public final class Preconditions {
    * @param expression a boolean expression
    * @throws IllegalStateException if {@code expression} is false
    */
+  @AssertMethod
   public static void checkState(boolean expression) {
     if (!expression) {
       throw new IllegalStateException();
@@ -453,6 +458,7 @@ public final class Preconditions {
    *     string using {@link String#valueOf(Object)}
    * @throws IllegalStateException if {@code expression} is false
    */
+  @AssertMethod
   public static void checkState(boolean expression, Object errorMessage) {
     if (!expression) {
       throw new IllegalStateException(String.valueOf(errorMessage));
diff --git a/java/pom.xml b/java/pom.xml
index 6b7192fd33..62e63d41a9 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -48,6 +48,7 @@
     <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
     <mockito.core.version>5.5.0</mockito.core.version>
     <mockito.inline.version>5.2.0</mockito.inline.version>
+    <checker.framework.version>3.42.0</checker.framework.version>
   </properties>
 
   <scm>
@@ -354,6 +355,7 @@
                 <!-- source annotations (not kept in compiled code) -->
                 
<ignoredDependency>javax.annotation:javax.annotation-api:*</ignoredDependency>
                 
<ignoredDependency>org.apache.hadoop:hadoop-client-api</ignoredDependency>
+                
<ignoredDependency>org.checkerframework:checker-qual</ignoredDependency>
               </ignoredDependencies>
             </configuration>
           </execution>
@@ -606,6 +608,11 @@
         <type>pom</type>
         <scope>import</scope>
       </dependency>
+      <dependency>
+        <groupId>org.checkerframework</groupId>
+        <artifactId>checker-qual</artifactId>
+        <version>${checker.framework.version}</version>
+      </dependency>
       <dependency>
         <groupId>com.google.flatbuffers</groupId>
         <artifactId>flatbuffers-java</artifactId>

Reply via email to