clob.getsubstring() 在大数据量时直接 oom,因为它会将整个 clob 内容一次性加载进 jvm 堆内存并转为 string,导致内存占用翻倍(如 100mb clob 占约 200mb 堆空间),引发 java.lang.outofmemoryerror。
为什么 clob.getsubstring() 在大数据量时直接 oom
因为 clob.getsubstring() 会把整个 clob 内容一次性加载进 jvm 堆内存,转成 string。哪怕数据库里是 100mb 的文本,它也会尝试分配同等大小的字符数组——而 java 中一个 char 占 2 字节,实际堆内存占用可能接近 200mb,远超常见应用的堆配置。
常见错误现象:java.lang.OutOfMemoryError: Java heap space,尤其在批量处理日志、XML 或 JSON 类 CLOB 字段时高频出现。
- 只适用于确认长度可控(如
- 底层依赖 JDBC 驱动是否真正支持“按需拉取”;Oracle 驱动对
getSubString()仍可能先 fetch 全量 LOB locator 再本地读 - 调用前必须用
clob.length()判断大小,但某些驱动(如旧版 MySQL Connector/J)对 CLOB 的length()本身就会触发全量加载
用 getCharacterStream() 流式读取才是正解
它返回 java.io.Reader,底层由驱动控制分块读取,不缓存全文,内存占用稳定在 KB 级别。这是 JDBC 规范推荐的 CLOB 大数据读法。
使用场景:导出报表正文、解析嵌套 JSON、逐行清洗日志内容。
- 务必配合
try-with-resources,否则 Reader 不关闭会导致数据库连接持有的 LOB 句柄泄漏(尤其 Oracle) - 不要用
readLine()直接读——CLOB 可能不含换行符,且BufferedReader.readLine()内部缓冲区不可控 - 推荐按固定 buffer 读取:
char[] buf = new char[8192]; int n; while ((n = reader.read(buf)) != -1) { /* 处理 buf[0..n) */ } - 注意字符集:数据库 CLOB 的编码未必是 UTF-8,需与
Connection或驱动参数(如useUnicode=true&characterEncoding=UTF-8)对齐
Oracle 驱动下特别要关掉 implicitCachingEnabled
Oracle JDBC 驱动默认开启隐式 LOB 缓存,导致即使你用了流式读,驱动仍可能在内部缓存整个 CLOB 内容,尤其是执行过 clob.length() 或 clob.getSubString(1,1) 后。
错误表现:内存曲线随记录数线性上涨,jmap -histo 显示大量 [C(char[])实例,但代码里没显式保存字符串。
- 在连接 URL 中显式禁用:
?implicitCachingEnabled=false - 或在
DataSource配置中设connectionProperties:implicitCachingEnabled=false - 避免任何对 CLOB 对象的“试探性”调用,例如为判空而写
clob != null && clob.length() > 0—— 改用ResultSet.wasNull()
MySQL 和 PostgreSQL 的 CLOB 行为差异
MySQL 实际没有标准 CLOB 类型,常用 TEXT / MEDIUMTEXT 映射为 java.sql.Clob,但 Connector/J 默认把 TEXT 当作普通字符串处理,getCharacterStream() 可能退化为全量加载。
PostgreSQL 使用 text 类型时通常不走 CLOB 接口,而是直接映射为 String;真要用 CLOB(如 pg_largeobject),需手动注册 PGClob 类型处理器。
- MySQL:强制启用流式需加参数
useServerPrepStmts=true&cachePrepStmts=true,并确保字段定义为MEDIUMTEXT以上 - PostgreSQL:优先用
ResultSet.getString()读text;若必须用 CLOB,确认驱动版本 ≥ 42.2.0,且连接参数含stringtype=unspecified - 跨库统一处理建议:封装一层
ClobReader工具类,根据DatabaseMetaData.getDatabaseProductName()分支选择策略
CLOB 读取的坑不在 API 多难,而在驱动实现和数据库特性的组合效应——同一段流式代码,在 Oracle 上稳如磐石,在 MySQL 上可能悄悄吃光内存。动手前先查清你用的驱动版本、数据库字段真实类型、连接参数是否生效,比死磕 API 文档管用得多。










