当前位置:首页 > 科技  > 软件

终于明白为啥面试老是有人问 SubList 了,原来这玩意会 OOM!

来源: 责编: 时间:2024-03-18 09:33:25 299观看
导读最近刚做到一个内存分页的需求,自测了几次就 OOM 了,找了半天原因,终于把这个坑填上来,下面整理一下发出来,各位小伙伴引以为鉴。我们经常会使用 List.subList 方法对 List 进行切片,比如取前十个元素出来用,但是和 Arrays.a

最近刚做到一个内存分页的需求,自测了几次就 OOM 了,找了半天原因,终于把这个坑填上来,下面整理一下发出来,各位小伙伴引以为鉴。wI828资讯网——每日最新资讯28at.com

我们经常会使用 List.subList 方法对 List 进行切片,比如取前十个元素出来用,但是和 Arrays.asList 的问题类似(具体文章可以看 慎用 ArrayList,全是坑!),List.subList 返回的子 List 不是一个全新地址的 ArrayList,这个子 List 会和原始 List 相互影响。wI828资讯网——每日最新资讯28at.com

如果不注意,很可能会因此产生 OOM 问题。wI828资讯网——每日最新资讯28at.com

话不多说,先复现问题。如下代码所示,定义一个名为 data 的静态 List 用来存放 List<Integer> 类型,循环 1000 次,每次都从一个具有 100 万个 Integer 的 List 中(即代码中的 rawList),使用 subList 方法获得一个只包含一个数字的子 List,并把这个子 List 加入 data 变量:wI828资讯网——每日最新资讯28at.com

图片图片wI828资讯网——每日最新资讯28at.com

看起来,这个 data 变量里面最终保存的只是 1000 个具有 1 个元素的 List 而已,并不会出现什么问题啊。wI828资讯网——每日最新资讯28at.com

但是,代码在运行到一段时间后,可以看到在我的机器上是第 159 次循环后发生了 OOM:wI828资讯网——每日最新资讯28at.com

图片图片wI828资讯网——每日最新资讯28at.com

出现 OOM 的原因是,循环中的 1000 个具有 100 万个元素的 List 始终得不到回收,因为它始终被 subList 方法返回的 List 强引用。wI828资讯网——每日最新资讯28at.com

subList 返回的子 List 为啥会强引用原始的 List?再来做个实验看下:wI828资讯网——每日最新资讯28at.com

首先初始化一个包含数字 1 到 10 的 ArrayList,然后通过调用 subList 方法取出 2、3、4,随后删除这个 SubList 中的元素数字 3。可以看到原始 List 中数字 3 被删除了,说明删除子 List 中的元素影响到了原始 List:wI828资讯网——每日最新资讯28at.com

图片图片wI828资讯网——每日最新资讯28at.com

图片图片wI828资讯网——每日最新资讯28at.com

继续看,我们为原始的 ArrayList 增加一个元素数字 0,然后遍历 SubList 输出所有元素。代码运行后报错 java.util.ConcurrentModificationException:wI828资讯网——每日最新资讯28at.com

图片图片wI828资讯网——每日最新资讯28at.com

图片图片wI828资讯网——每日最新资讯28at.com

分析下 ArrayList 的源码,看看为什么会是这样:wI828资讯网——每日最新资讯28at.com

图片图片wI828资讯网——每日最新资讯28at.com

  1. ArrayList 维护了一个 int 类型的 modCount 的字段,表示 List 结构性修改的次数。所谓结构性修改,指的是影响 List 大小的修改,所以 add 操作必然会改变 modCount 的值。
  2. 分析 subList 方法可以看到,获得的 List 其实是创建了一个内部类 SubList,并不是普通的 ArrayList。
  3. 在初始化内部类 SubList 的时候传入了 this,这个 SubList 中的 parent 字段就是原始的 List,初始化的时候,并没有把原始 List 中的元素复制到独立的变量中保存,所以双方对元素的修改都会互相影响。而且 SubList 强引用了原始的 List,所以大量保存这样的 SubList 其实也保存了大量原始的 List,从而导致 OOM。
  4. 分析 listIterator 方法可知,遍历 SubList 的时候会先获得迭代器,比较原始 ArrayList modCount 的值和 SubList 当前 modCount 的值,如果不想等,就会抛出 ConcurrentModificationException 异常。所以上述实验代码,我们在获得了 SubList 为原始 List 新增了一个元素,修改了原始 List 的 modCount,所以判等失败抛出异常。

综上,既然 SubList 和原始 List 会相互影响,那么避免相互影响的修复方式有两种:wI828资讯网——每日最新资讯28at.com

  1. 不直接使用 subList 方法返回的 SubList,而是重新使用 new ArrayList,在构造方法传入 SubList,来构建一个独立的 ArrayList:
List<Integer> subList = new ArrayList<>(list.subList(1, 4));
  1. 对于 Java 8 使用 Stream 的 skip 和 limit API 来跳过流中的元素,以及限制流中元素的个数,同样可以达到 SubList 切片的目的:
List<Integer> subList = list.stream().skip(1).limit(3).collect(Collectors.toList());

wI828资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-76481-0.html终于明白为啥面试老是有人问 SubList 了,原来这玩意会 OOM!

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 用 React/Vue 不如用 JQuery,你知道吗?

下一篇: SpringCloud微服务中如何实现多端认证?

标签:
  • 热门焦点
  • Mate60手机壳曝光 致敬自己的经典设计

    8月3日消息,今天下午博主数码闲聊站带来了华为Mate60的第三方手机壳图,可以让我们在真机发布之前看看这款华为全新旗舰的大致轮廓。从曝光的图片看,Mate 60背后摄像头面积依然
  • 2023年Q2用户偏好榜:12+256G版本成新主流

    3月份的性能榜、性价比榜和好评榜之后,就要轮到2023年的第二季度偏好榜了,上半年的新机潮已经过去,最明显的肯定就是大内存和存储的机型了,另外部分中端机也取消了屏幕塑料支架
  • 线程通讯的三种方法!通俗易懂

    线程通信是指多个线程之间通过某种机制进行协调和交互,例如,线程等待和通知机制就是线程通讯的主要手段之一。 在 Java 中,线程等待和通知的实现手段有以下几种方式:Object 类下
  • 一年经验在二线城市面试后端的经验分享

    忠告这篇文章只适合2年内工作经验、甚至没有工作经验的朋友阅读。如果你是2年以上工作经验,请果断划走,对你没啥帮助~主人公这篇文章内容来自 「升职加薪」星球星友 的投稿,坐
  • 大厂卷向扁平化

    来源:新熵作者丨南枝 编辑丨月见大厂职级不香了。俗话说,兵无常势,水无常形,互联网企业调整职级体系并不稀奇。7月13日,淘宝天猫集团启动了近年来最大的人力制度改革,目前已形成一
  • 8月见!小米MIX Fold 3获得3C认证:支持67W快充

    这段时间以来,包括三星、一加、荣耀等等有不少品牌旗下的最新折叠屏旗舰都得到了不少爆料,而小米新一代折叠屏旗舰——小米MIX Fold 3此前也屡屡被传
  • 华为Mate 60保护壳曝光:硕大后置相机模组 凸起程度有惊喜

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
  • 电博会上海尔智家模拟500平大平层,还原生活空间沉浸式体验

    电博会为了更好地让参展观众真正感受到智能家居的绝妙之处,海尔智家的程传岭先生同样介绍了展会上海尔智家的模拟500平大平层,还原生活空间沉浸式体验。程传
  • Meta盲目扩张致超万人被裁,重金押注元宇宙而前景未明

    图片来源:图虫创意日前,Meta创始人兼CEO 马克&middot;扎克伯发布公开信,宣布Meta计划裁员超11000人,占其员工总数13%。他公开承认了自己的预判失误:&ldquo;不仅
Top