拆分大对象 + 流式处理 + 不一次性加载全量数据

目录

一、先讲核心思想(一句话记住)

二、为什么要这么优化?(生产血泪教训)

坏代码的后果:

好代码的效果:

三、三个优化点详细讲解

[1. 拆分大对象(Split Large Objects)](#1. 拆分大对象(Split Large Objects))

什么是大对象?

为什么危险?

怎么拆分?

[示例:坏代码 vs 好代码](#示例:坏代码 vs 好代码)

[❌ 坏代码(一次性加载)](#❌ 坏代码(一次性加载))

[✅ 好代码(分批加载 + 拆分)](#✅ 好代码(分批加载 + 拆分))

[2. 流式处理(Stream / 流式读取)](#2. 流式处理(Stream / 流式读取))

什么是流式处理?

优势:

[Java 中两种流式写法:](#Java 中两种流式写法:)

[① 数据库流式查询(MyBatis / JDBC)](#① 数据库流式查询(MyBatis / JDBC))

[✅ 正确写法(MyBatis 流式查询)](#✅ 正确写法(MyBatis 流式查询))

[② Java8 Stream 流式处理(不存大 List)](#② Java8 Stream 流式处理(不存大 List))

[❌ 坏代码](#❌ 坏代码)

[✅ 流式处理(不创建临时大集合)](#✅ 流式处理(不创建临时大集合))

[3. 不一次性加载全量数据(核心!)](#3. 不一次性加载全量数据(核心!))

错误做法:

正确做法:

[示例:Excel 导入优化(超级经典)](#示例:Excel 导入优化(超级经典))

[❌ 坏代码(一次性读取全部行)](#❌ 坏代码(一次性读取全部行))

[✅ 好代码(流式逐行读取)](#✅ 好代码(流式逐行读取))

四、三合一终极实战代码(生产标准模板)

[场景:导出 10 万用户数据(不 OOM、不卡顿)](#场景:导出 10 万用户数据(不 OOM、不卡顿))

这个代码的优势:

五、这三个优化解决了什么生产问题?

六、最简单记忆口诀


这是生产环境解决 OOM、频繁 GC、大对象卡顿终极三板斧 。本文会用最通俗的语言 + 真实业务场景 + 可直接复制的代码,把这三个优化点讲透。


一、先讲核心思想(一句话记住)

不要把所有数据一次性全部加载到内存里,不要创建超大对象 / 超大集合,要像 "水管流水" 一样,读一点、处理一点、释放一点。

这就是:流式处理 + 分页 / 游标读取 + 小对象处理


二、为什么要这么优化?(生产血泪教训)

坏代码的后果:

  1. 一次性加载 10w 条数据到 List → 占几百 MB 内存
  2. 超大对象(10MB+) → 直接进入老年代
  3. FullGC 频繁 → 接口超时、服务卡死
  4. 最终 OOM 崩溃

好代码的效果:

  • 内存占用始终稳定
  • 无大对象
  • YGC 少,FGC 几乎为 0
  • 处理百万数据也不崩

三、三个优化点详细讲解

1. 拆分大对象(Split Large Objects)

什么是大对象?

  • 单个对象 > 10MB
  • 超大 List/Map 装几万条数据
  • 一次性加载整个文件、Excel、大报文

为什么危险?

JVM 规则:大对象直接进入老年代,老年代满了就触发 FullGC。

怎么拆分?

  1. 大集合拆成小批量(100~1000 条一批)
  2. 大报文拆字段,不需要的不加载
  3. 大文件分段读,不一次性读入内存

示例:坏代码 vs 好代码

❌ 坏代码(一次性加载)
复制代码
// 一次性加载10万条,占巨大内存
List<User> userList = userMapper.selectAll(); 
for(User user : userList){
    // 处理
}
✅ 好代码(分批加载 + 拆分)
复制代码
// 每次只查1000条
int pageSize = 1000;
int pageNum = 1;
while(true){
    List<User> userList = userMapper.selectPage(pageNum, pageSize);
    if(userList.isEmpty()) break;
    
    // 处理小批量数据
    processList(userList);
    
    pageNum++;
}

2. 流式处理(Stream / 流式读取)

什么是流式处理?

数据像水流一样,来一条处理一条,不全部存内存。

优势:

  • 内存占用极低
  • 无大集合
  • 处理完立即释放

Java 中两种流式写法:

① 数据库流式查询(MyBatis / JDBC)

✅ 正确写法(MyBatis 流式查询)
复制代码
// Mapper 接口(注解方式)
@Select("SELECT * FROM user")
@ResultType(User.class)
Cursor<User> selectAllUserStream(); // Cursor = 流

// 业务代码:逐条读取,不加载全量
try (Cursor<User> cursor = userMapper.selectAllUserStream()) {
    for (User user : cursor) {
        // 逐条处理,内存只存1条
        handleUser(user);
    }
}

内存几乎不涨!


② Java8 Stream 流式处理(不存大 List)

❌ 坏代码
复制代码
List<String> nameList = new ArrayList<>();
for(User user : userList){
    nameList.add(user.getName());
}
✅ 流式处理(不创建临时大集合)
复制代码
userList.stream()
       .map(User::getName)
       .forEach(this::sendMessage); // 直接处理,不存储

3. 不一次性加载全量数据(核心!)

错误做法:

复制代码
List data = queryAll(); // 全量加载 → OOM源头

正确做法:

  1. 分页查询
  2. 流式查询
  3. 游标 / 迭代器读取
  4. 文件分段读取

示例:Excel 导入优化(超级经典)

❌ 坏代码(一次性读取全部行)
复制代码
List<Row> rows = sheet.getRows(); // 加载10万行到内存
for(Row row : rows){
    // 处理
}
✅ 好代码(流式逐行读取)
复制代码
// 一行行读,读完即释放,内存只存一行
ExcelReader reader = ExcelUtil.getReader(file);
reader.read(this::processRow); // 流式消费

// 处理方法
void processRow(UserRow row){
    // 处理单行
}

四、三合一终极实战代码(生产标准模板)

场景:导出 10 万用户数据(不 OOM、不卡顿)

复制代码
@Transactional(readOnly = true) // 流式查询必须开启事务
public void exportUser(OutputStream outputStream){
    // 1. 数据库流式查询(不加载全量)
    try (Cursor<User> cursor = userMapper.streamAllUser()) {
        
        // 2. 流式写入Excel(不创建大对象)
        ExcelWriter writer = ExcelUtil.getWriter(outputStream);
        writer.write(cursor.stream()  // 3. Java流式处理
                     .map(this::convertToVO) 
                     .peek(item -> {
                         // 逐条处理
                     }));
        
        writer.flush();
    }
}

这个代码的优势:

  • 内存永远只占用几十 KB
  • 不会产生大对象
  • 不会频繁 GC
  • 处理 100 万数据也不会 OOM

五、这三个优化解决了什么生产问题?

  1. OOM
  2. 频繁 FullGC
  3. 大对象导致卡顿
  4. 内存持续上涨
  5. 大数据量接口超时

六、最简单记忆口诀

不一次性加载, 不创建大对象, 用流式一条条处理。

相关推荐
不爱编程的小陈4 分钟前
操作系统内存管理 —— 虚拟内存
jvm
kuonyuma15 分钟前
java之动态代理
java·开发语言
衔烛之龙117 分钟前
Windows x64 构建 liboqs-java教程
java·windows·python
一路向北North26 分钟前
Spring Security OAuth2.0(20):完善环境配置
java·后端·spring
Java面试题总结26 分钟前
IntelliJ IDEA 从卡顿到起飞,只用改这些。。。
java·ide·intellij-idea
兰令水31 分钟前
hot100【acm版】【2026.7.11/12打卡-java版本】
java·开发语言·数据结构·算法·职场和发展
Misnearch1 小时前
nacos配置管理改造
java·spring cloud·nacos
XWalnut1 小时前
LeetCode刷题 day29
java·算法·leetcode
4154111 小时前
MyBatis-Plus + PostGIS 实战(1.1):Geometry 字段在 Swagger 中的优雅展示
java·mybatis·postgis
极客先躯2 小时前
高级java每日一道面试题-2026年04月05日-实战篇[Docker]-如何排查容器 OOM 的问题?
java·运维·docker·容器·内存溢出·高级面试