从Oracle表获取25MB CLOB报OOM,求SpringBoot解决方案
问题描述
尝试从Oracle SQL表中获取约25MB的CLOB数据时,遇到以下错误:
WARNING: Resolved [org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: Java heap space]
当前使用的SpringBoot代码如下:
public byte[] getData(String task) { StringBuilder sb = new StringBuilder(); sb.append("select OUTPUT_file from tablea where task = ?"); byte[] fileBytes = jdbcTemplate.query(sb.toString(), new Object[]{task}, (rs) ->{ if(rs.next()){ byte[] resByteArry =new byte[0]; Clob clob = rs.getClob("OUTPUT_file"); resByteArry =getByteArrayByStream(clob.getAsciiStream()); return resByteArry; }else { return new byte[0]; } }); return fileBytes; } private byte[] getByteArrayByStream(InputStream ins) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { byte[] buffer = new byte[1024]; int length =0; while( (length = ins.read(buffer,0,buffer.length)) !=-1){ out.write(buffer,0,length); } out.flush(); out.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return out.toByteArray(); }
请问是否有办法成功获取该文件?同时求基于SpringBoot的其他替代实现方案。
解决方案
临时修复:调整JVM堆内存
如果只是临时解决这个问题,可以在启动SpringBoot应用时增加堆内存参数,比如:
java -jar your-app.jar -Xmx512m
将-Xmx512m调整到足够容纳25MB数据的大小即可,这样能一次性把CLOB数据加载到内存中。但这种方法仅适用于小体量CLOB,处理更大数据时仍会触发OOM,且可能引发其他内存相关问题。
最优方案:流式处理,避免全量数据加载
你的代码核心问题是将整个CLOB转换成byte[]存入内存,导致堆内存溢出。最优方案是采用流式处理,直接边读边输出/处理数据,不把全量数据加载到内存中。
基于SpringBoot的流式实现示例
1. 直接向客户端流式返回数据
修改接口方法,通过StreamingResponseBody将CLOB数据直接流式输出给客户端,无需转换成byte[]:
import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; @GetMapping("/data/{task}") public ResponseEntity<StreamingResponseBody> getDataStream(@PathVariable String task) { StreamingResponseBody responseBody = outputStream -> { String sql = "select OUTPUT_file from tablea where task = ?"; jdbcTemplate.query(sql, new Object[]{task}, rs -> { if (rs.next()) { try (InputStream inputStream = rs.getClob("OUTPUT_file").getAsciiStream()) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); outputStream.flush(); } } catch (IOException e) { throw new RuntimeException("读取CLOB数据失败", e); } } }); }; return ResponseEntity.ok() .header("Content-Type", "application/octet-stream") .body(responseBody); }
2. 流式写入本地文件
如果需要将CLOB数据写入本地文件,同样可以采用流式处理:
import java.io.FileOutputStream; import java.io.InputStream; public void writeClobToFile(String task, String filePath) { String sql = "select OUTPUT_file from tablea where task = ?"; jdbcTemplate.query(sql, new Object[]{task}, rs -> { if (rs.next()) { try (InputStream inputStream = rs.getClob("OUTPUT_file").getAsciiStream(); FileOutputStream outputStream = new FileOutputStream(filePath)) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } } catch (IOException e) { throw new RuntimeException("写入文件失败", e); } } }); }
额外优化建议
- 调整缓冲区大小:将原来的1024字节缓冲区改为4096或8192字节,减少IO交互次数,提升处理效率。
- 强制使用
try-with-resources:自动关闭流资源,避免资源泄漏,上述示例已采用该语法。 - 更新Oracle JDBC驱动:确保使用最新版本的Oracle JDBC驱动,旧版本处理大CLOB时可能存在内存泄漏问题。
内容的提问来源于stack exchange,提问作者user1015388
相关产品推荐
相关产品推荐

