Java中substring()在JDK 6及之前存在内存泄漏风险,因共享原char[]数组;JDK 7u6起改为复制字符,彻底修复该问题,现代JDK(8/11/17/21)均安全。

Java 中 String.substring() 方法在 JDK 6 及之前版本确实存在内存泄露风险,但该问题在 JDK 7u6 之后已被彻底修复,现代 JDK(8、11、17、21)中不再存在此隐患。
JDK 6 及更早:底层共享 char[] 导致内存无法释放
在 JDK 6 中,substring() 并不创建新的字符数组,而是复用原字符串的 char[],仅通过偏移量(offset)和长度(count)构建新 String 对象。这意味着即使只取几个字符的子串,只要该子串对象还存活,整个原始大字符串的底层 char[] 就无法被 GC 回收。
例如:
String huge = new String(new char[1024 * 1024]); // 1MB 字符数组String small = huge.substring(0, 10); // 仍持有对 huge 的 char[] 的强引用
此时 small 持有对巨大数组的引用,可能导致堆内存持续占用,尤其在缓存场景下易引发 OOM。
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
JDK 7u6 起:substring 改为复制字符内容
从 JDK 7 update 6(2012年发布)开始,OpenJDK 和 Oracle JDK 均将 substring() 实现改为显式拷贝所需字符到新 char[](或 JDK 9+ 的 byte[]),彻底切断与原字符串底层数组的关联。
关键变化包括:
- 不再依赖
offset和count字段 - 构造新 String 时调用
Arrays.copyOfRange()或等效逻辑 - 原字符串即使很大,其
char[]在无其他引用时可被正常回收
JDK 9+:进一步优化 —— 使用 byte[] 和 coder 字段
从 JDK 9 开始,String 底层改用 byte[] + coder(标识 Latin-1 或 UTF-16 编码),substring() 同样执行紧凑拷贝,且支持更省空间的单字节编码路径。内存行为更可控,不存在历史遗留的共享数组问题。
需要注意的是:
- 所有主流现代 JDK(8u121+、9+、11、17、21)均采用安全实现
- 若项目仍在使用 JDK 6 或非常旧的 JDK 7(如 u0~u5),应尽快升级
- 即便在老版本中,也可手动规避:用
new String(str.substring(...))强制触发数组拷贝(但仅限紧急兼容)
实际开发建议
无需为 substring 写额外防御代码,但需确保运行环境符合最低安全版本:
- 生产环境至少使用 JDK 8u121 或 JDK 11+
- 避免在 JDK 6/7u5 环境下长期缓存 substring 结果(尤其是从大日志、JSON、XML 中提取的小字段)
- 使用 JFR 或 MAT 分析堆转储时,若发现大量未释放的
char[]且被 String 引用,可检查是否误用老旧 JDK

















