From 356032d437cfcc36841abb0f5b43ecf3ed4ebfa2 Mon Sep 17 00:00:00 2001 From: Christopher Tubbs Date: Sat, 1 Aug 2026 00:32:32 -0400 Subject: [PATCH] Limit memory usage when deserializing Mutations --- .../apache/accumulo/core/data/Mutation.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/apache/accumulo/core/data/Mutation.java b/core/src/main/java/org/apache/accumulo/core/data/Mutation.java index 95ee6f4120d..3a15e5d94c4 100644 --- a/core/src/main/java/org/apache/accumulo/core/data/Mutation.java +++ b/core/src/main/java/org/apache/accumulo/core/data/Mutation.java @@ -73,6 +73,13 @@ */ public class Mutation implements Writable { + // the exact upper boundary for the initial array size doesn't matter, so long as it's high enough + // to avoid resizing if the user has any reasonable number of column updates in a single mutation; + // this value, near 100_000, was chosen to try to optimize memory allocation to typical hardware + // page sizes, accounting for 16 bytes overhead for the array, that works with either 32-bit + // compressed object references or native 64-bit references, while keeping the value reasonable + private static final int MAX_INITIAL_ARRAY_SIZE = 100_348; + /** * Internally, this class keeps most mutation data in a byte buffer. If a cell value put into a * mutation exceeds this size, then it is stored in a separate buffer, and a reference to it is @@ -1255,19 +1262,22 @@ private byte[] readBytes(UnsynchronizedBuffer.Reader in) { public List getUpdates() { serialize(); - UnsynchronizedBuffer.Reader in = new UnsynchronizedBuffer.Reader(data); - if (updates == null) { + var in = new UnsynchronizedBuffer.Reader(data); + if (entries == 1) { updates = Collections.singletonList(deserializeColumnUpdate(in)); } else { - ColumnUpdate[] tmpUpdates = new ColumnUpdate[entries]; + // if the number of column updates is excessive, then the performance will be slowed due to + // resizing the ArrayList to meet the requested capacity + int initialArraySize = Math.min(entries, MAX_INITIAL_ARRAY_SIZE); + var tmpUpdates = new ArrayList(initialArraySize); for (int i = 0; i < entries; i++) { - tmpUpdates[i] = deserializeColumnUpdate(in); + tmpUpdates.add(deserializeColumnUpdate(in)); } - updates = Arrays.asList(tmpUpdates); + updates = tmpUpdates; } }