Backend Development 9 min read

Handling Java Collection OutOfMemoryError: Code Optimization and Server Configuration

This article explains why collection‑level OutOfMemoryError occurs in Java services, presents two core solutions—code‑level pagination using Stream and database queries, plus hardware upgrades and cloud server scaling—to prevent heap overflow and improve backend performance.

Java Captain
Java Captain
Java Captain
Handling Java Collection OutOfMemoryError: Code Optimization and Server Configuration

Preface

OOM is one of the most common online bugs encountered in production; when a normally functioning page crashes or a service becomes unavailable, the server logs often contain "Caused by: java.lang.OutOfMemoryError: Java heap space", indicating a Java heap overflow.

Among these, collection memory overflow is the most frequent. Developers may inadvertently load an entire database table into a List , assign unfiltered data to a Set for deduplication, or create massive collection objects under high concurrency without releasing them, causing the JVM to fail to reclaim memory.

1. Code Optimization

The solution consists of two main ideas: first, optimize the code; second, configure the hardware reasonably.

1.1 Stream pagination

Use Java Stream's skip() and limit() to implement self‑pagination, fetching data in fixed batches until all records are retrieved or a condition is met.

/**
 * The following example methods are all placed in this implementation class, including inheritance and interface implementation
 */
@Service
public class StudyServiceImpl extends ServiceImpl
implements StudyService {}

/**
 * Avoid collection memory overflow method (1)
 * @return
 */
private List
getList(){
    ArrayList
resultList = new ArrayList<>();
    // 1. Retrieve only the id field from the database to avoid overflow
    List
idsList = this.list(new LambdaQueryWrapper
()
        .select(Study::getId)).stream()
        .map(Study::getId)
        .collect(Collectors.toList());
    boolean loop = true;
    long number = 0;
    long perSize = 5000;
    while(loop){
        List
ids = idsList.stream()
            .skip(number * perSize)
            .limit(perSize)
            .collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(ids)){
            List
voList = this.listByIds(ids).stream()
                .map(e -> e.copyProperties(StudyVO.class))
                .collect(Collectors.toList());
            resultList.addAll(voList);
        }
        number++;
        loop = ids.size() == perSize;
    }
    return resultList;
}

1.2 Database pagination

Query a fixed number of rows from the database in each loop; stop when the result set is smaller than the page size.

/**
 * Avoid collection memory overflow method (2)
 * @param param
 * @return
 */
private List
getList(String param){
    ArrayList
resultList = new ArrayList<>();
    String id = "";
    boolean loop = true;
    int perSize = 5000;
    while(loop){
        Page
studyPage = this.page(new Page<>(NumberUtils.INTEGER_ZERO, perSize),
            wrapperBuilder(param, id));
        if(Objects.nonNull(studyPage)){
            List
studyList = studyPage.getRecords();
            if(CollectionUtils.isNotEmpty(studyList)){
                id = studyList.get(perSize - NumberUtils.INTEGER_ONE).getId();
                loop = studyList.size() == perSize;
                resultList.addAll(studyList.stream()
                    .map(e -> e.copyProperties(StudyVO.class))
                    .collect(Collectors.toList()));
            } else {
                loop = false;
            }
        }
    }
    return resultList;
}

/**
 * Condition builder
 * @param param
 * @param id
 * @return
 */
private LambdaQueryWrapper
wrapperBuilder(String param, String id){
    LambdaQueryWrapper
wrapper = new LambdaQueryWrapper<>();
    wrapper.select(Study::getUserAvatar)
        .eq(Study::getOpenId, param)
        .orderByAsc(Study::getId);
    if(StringUtils.isNotBlank(id)){
        // Only fetch records with id greater than the current one
        wrapper.gt(Study::getId, id);
    }
    return wrapper;
}

1.3 Other considerations

Even with pagination, loading massive data into memory (e.g., 500k rows) remains risky. Additional strategies include:

Business decomposition: evaluate whether the backend truly needs to process such large datasets and consider alternative UI interactions.

Database design: decouple tables, keep field granularity fine, and query only required columns.

Disk storage: use message queues or other storage and fetch data in fixed batches, releasing resources promptly.

Asynchronous batch processing: for non‑real‑time requirements, write data to file streams and store in object storage.

Scheduled tasks: discuss with product managers whether synchronous processing is mandatory; if not, spread the work over time.

Consult the big‑data team for professional handling of massive data.

2. Hardware Configuration

The core idea is to increase server memory, allocate heap memory reasonably, and set up elastic scaling rules so that alerts trigger automatic expansion, ensuring system availability.

2.1 Cloud server configuration

Below is a screenshot of the Alibaba Cloud ECS management console where CPU and memory can be configured. After creating an ECS auto‑scaling group, a custom scaling policy can be defined to automatically add instances when traffic spikes.

If the deployment is on a private cloud, JVM parameters may need fine‑tuning by senior engineers or the operations team.

3. Article Summary

This article records the handling process for a collection‑level memory overflow incident in a production Java service. Future posts will cover high‑concurrency handling, cache usage, and asynchronous/de‑coupling techniques.

That concludes today’s sharing; any mistakes or omissions are welcome for correction, and readers are invited to comment and discuss.

backendJavaPerformanceMemoryOptimizationOutOfMemoryErrorDatabasePaginationServerConfiguration
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.