找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2417

积分

0

好友

319

主题
发表于 1 小时前 | 查看: 1| 回复: 0

今天不聊太枯燥的底层源码,分享一个写业务代码时容易踩到的“小坑”。

我们经常会用到 subList 来截取集合,这本身没问题。但在某些特定场景下,如果用法随意,很容易引发隐蔽的内存泄漏,等线上问题暴露出来时就晚了。

举个最典型的真实场景:
你需要对接一个不受控制的第三方老接口,它不支持分页,每次调用都会把几千条全量数据全部返回。而你自己的业务只需要取前10条数据做首页展示,并且为了提升性能,会把这10条数据放到本地缓存里。

代码可能很自然地就这么写了:

// 从第三方老接口拉取全量数据,假设有 5000 条
List<String> allData = thirdPartyApi.getAllData();

// 截取前 10 条
List<String> top10 = allData.subList(0, 10);

// 放入本地长生命周期的缓存中
LocalCache.put("home_top_10", top10);

这段代码在本地运行一切正常,拿到的确实只有10条数据。但如果这个逻辑每隔几分钟就执行一次,线上服务器的内存占用就会像温水煮青蛙一样慢慢涨上去,最终可能触发 OOM。

为什么仅仅10条数据会引起内存泄漏?

问题的根源藏在 subList 的底层实现里。我们看一眼 ArrayList 的源码就明白了:

public List<E> subList(int fromIndex, int toIndex) {
    // ... 省略越界检查

    // 注意看这里,返回的是一个内部类 SubList,并且把 this (原集合) 传了进去
    return new SubList<>(this, fromIndex, toIndex);
}

// ArrayList 的内部类
private class SubList<E> extends AbstractList<E> implements RandomAccess {
    private final ArrayList<E> parent; // 👈 致命的引用在这里

    SubList(ArrayList<E> parent, int offset, int fromIndex, int toIndex) {
        // 这个内部类保留了原大集合的引用
        this.parent = parent; 
        // ...
    }
}

看出来了吗?ArrayListsubList 方法,并没有真正创建一个只包含截取数据的新集合。它返回的是一个内部“视图”类(SubList)。这个视图类通过 this.parent = parent; 这行代码,牢牢持有了对原集合 allData 的强引用。

这意味着:当你把截取出来的 top10 放进长生命周期的缓存后,这10条数据会一直存活。而它的底层(SubList 对象)又死死拽着那5000条数据的原集合 allData,导致垃圾回收器(GC)根本无法回收那个庞大的集合。表面上你只缓存了10条数据,实际上内存里被迫保留了整整5000条。这种设计在处理大数据集和缓存时,尤其需要警惕。

正确的解决办法是什么?

最简单的方案,就是在截取之后,用一个新的 ArrayList 包装一下,创建一个全新的对象,彻底切断它与原集合的引用关系:

// 推荐写法:新建一个ArrayList,彻底切断底层引用
List<String> top10 = new ArrayList<>(allData.subList(0, 10));

如果你习惯使用 Java 8 的 Stream API(或者需要顺便做一些过滤、映射操作),也可以这样写:

List<String> top10 = allData.stream()
                            .limit(10)
                            .collect(Collectors.toList());

subList 引发的这种内存泄漏,在编写代码和跑单元测试时基本发现不了。它通常是在系统平稳运行一段时间后,随着内存被逐渐蚕食才会暴露问题,排查起来相当费劲。

建议大家,尤其是Java开发者,周末有空时可以全局搜一下自己项目里的 .subList 调用。如果发现截取后的集合被作为返回值长期持有,或者被存进了缓存、静态变量等长生命周期的地方,顺手给它套个 new ArrayList<>() 改掉吧。

一个小小的习惯,就能避免一个潜在的大坑。这个问题也欢迎大家在云栈社区分享和交流更多的“坑”与解决方案。

最后,祝大家编码愉快,周末开心!

玫瑰花表情包




上一篇:深入浅出C++函数模板:从推导规则到嵌入式实战应用
下一篇:API安全漏洞防御指南:企业如何应对越权访问、数据泄露与自动化攻击
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-22 05:14 , Processed in 0.672922 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表