Why ArrayList.subList Can Leak Memory, Loop Forever, and Crash Your Java App
This article explains how improper use of Java's ArrayList.subList can cause hidden memory leaks, infinite loops, ConcurrentModificationExceptions, and serialization failures in RPC frameworks, and provides concrete best‑practice solutions such as copying to a new list or using stream operations.
1. Memory leak caused by misuse
Running the sample code that repeatedly calls ArrayList.subList creates a SubList object that holds a reference to the original ArrayList. Each SubList keeps the original list in the old generation, so the allFailedList grows until the heap is exhausted even though each individual list only occupies a few megabytes.
public class OrderService {
public static void main(String[] args) {
OrderService orderService = new OrderService();
orderService.process();
}
public void process() {
List<Long> orderIdList = queryOrder();
List<List<Long>> allFailedList = new ArrayList<>();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
System.out.println(i);
List<Long> failedList = doProcess(orderIdList);
allFailedList.add(failedList);
}
}
private List<Long> doProcess(List<Long> orderIdList) {
List<Long> failedList = new ArrayList<>();
for (Long orderId : orderIdList) {
if (orderId % 2 == 0) {
failedList.add(orderId);
}
}
return failedList.subList(0, 1);
}
private List<Long> queryOrder() {
List<Long> orderIdList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
orderIdList.add(RandomUtils.nextLong());
}
return orderIdList;
}
}The subList implementation creates a SubList that stores a reference to the parent list; therefore every call adds another large object to the old generation.
2. Infinite loop caused by misuse
When a SubList is modified, its add method forwards the operation to the original list, which increases the original list’s size. If the loop iterates over arrayList.size() while the size keeps growing, the loop never terminates.
public class SubListDemo {
public static void main(String[] args) {
List<Long> arrayList = init();
List<Long> subList = arrayList.subList(0, 1);
for (int i = 0; i < arrayList.size(); i++) {
if (arrayList.get(i) % 2 == 0) {
subList.add(arrayList.get(i));
}
}
}
private static List<Long> init() {
List<Long> arrayList = new ArrayList<>();
arrayList.add(RandomUtils.nextLong());
arrayList.add(RandomUtils.nextLong());
arrayList.add(RandomUtils.nextLong());
arrayList.add(RandomUtils.nextLong());
arrayList.add(RandomUtils.nextLong());
return arrayList;
}
}3. Structural modification restrictions
Modifying a SubList after the parent list has been structurally changed triggers a ConcurrentModificationException. The exception originates from the checkForComodification method, which compares the parent list’s modCount with the sub‑list’s copy.
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}The JDK documentation defines modCount as the number of structural modifications (operations that change the list size).
4. Serialization failure in RPC
Because ArrayList.SubList does not implement Serializable, attempts to serialize a sub‑list as an RPC argument (e.g., with Dubbo) will fail.
5. Best practices
5.1 Copy to a new list
ArrayList<Object> myArrayList = new ArrayList<>();
ArrayList<Object> part1 = new ArrayList<>(myArrayList.subList(0, 25));
ArrayList<Object> part2 = new ArrayList<>(myArrayList.subList(26, 51));5.2 Use stream operations
dataList.stream()
.skip(5).limit(10)
.collect(Collectors.toList());
dataList.stream()
.skip(30).limit(10)
.collect(Collectors.toList());In summary, ArrayList.subList is intended as a read‑only view. Using it for write‑heavy scenarios leads to memory leaks, endless loops, concurrent‑modification errors, and serialization problems. Prefer copying to a new list or leveraging Java 8 streams for safe, efficient sub‑list handling.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
