自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

終于明白為啥面試?yán)鲜怯腥藛?SubList 了,原來這玩意會 OOM!

開發(fā) 前端
在初始化內(nèi)部類 SubList 的時候傳入了 this,這個 SubList 中的 parent 字段就是原始的 List,初始化的時候,并沒有把原始 List 中的元素復(fù)制到獨(dú)立的變量中保存,所以雙方對元素的修改都會互相影響。而且 SubList 強(qiáng)引用了原始的 List,所以大量保存這樣的 SubList 其實(shí)也保存了大量原始的 List,從而導(dǎo)致 OOM。

最近剛做到一個內(nèi)存分頁的需求,自測了幾次就 OOM 了,找了半天原因,終于把這個坑填上來,下面整理一下發(fā)出來,各位小伙伴引以為鑒。

我們經(jīng)常會使用 List.subList 方法對 List 進(jìn)行切片,比如取前十個元素出來用,但是和 Arrays.asList 的問題類似(具體文章可以看 慎用 ArrayList,全是坑?。?,List.subList 返回的子 List 不是一個全新地址的 ArrayList,這個子 List 會和原始 List 相互影響。

如果不注意,很可能會因此產(chǎn)生 OOM 問題。

話不多說,先復(fù)現(xiàn)問題。如下代碼所示,定義一個名為 data 的靜態(tài) List 用來存放 List<Integer> 類型,循環(huán) 1000 次,每次都從一個具有 100 萬個 Integer 的 List 中(即代碼中的 rawList),使用 subList 方法獲得一個只包含一個數(shù)字的子 List,并把這個子 List 加入 data 變量:

圖片圖片

看起來,這個 data 變量里面最終保存的只是 1000 個具有 1 個元素的 List 而已,并不會出現(xiàn)什么問題啊。

但是,代碼在運(yùn)行到一段時間后,可以看到在我的機(jī)器上是第 159 次循環(huán)后發(fā)生了 OOM:

圖片圖片

出現(xiàn) OOM 的原因是,循環(huán)中的 1000 個具有 100 萬個元素的 List 始終得不到回收,因為它始終被 subList 方法返回的 List 強(qiáng)引用。

subList 返回的子 List 為啥會強(qiáng)引用原始的 List?再來做個實(shí)驗看下:

首先初始化一個包含數(shù)字 1 到 10 的 ArrayList,然后通過調(diào)用 subList 方法取出 2、3、4,隨后刪除這個 SubList 中的元素數(shù)字 3??梢钥吹皆?List 中數(shù)字 3 被刪除了,說明刪除子 List 中的元素影響到了原始 List:

圖片圖片

圖片圖片

繼續(xù)看,我們?yōu)樵嫉?ArrayList 增加一個元素數(shù)字 0,然后遍歷 SubList 輸出所有元素。代碼運(yùn)行后報錯 java.util.ConcurrentModificationException:

圖片圖片

圖片圖片

分析下 ArrayList 的源碼,看看為什么會是這樣:

圖片圖片

  1. ArrayList 維護(hù)了一個 int 類型的 modCount 的字段,表示 List 結(jié)構(gòu)性修改的次數(shù)。所謂結(jié)構(gòu)性修改,指的是影響 List 大小的修改,所以 add 操作必然會改變 modCount 的值。
  2. 分析 subList 方法可以看到,獲得的 List 其實(shí)是創(chuàng)建了一個內(nèi)部類 SubList,并不是普通的 ArrayList。
  3. 在初始化內(nèi)部類 SubList 的時候傳入了 this,這個 SubList 中的 parent 字段就是原始的 List,初始化的時候,并沒有把原始 List 中的元素復(fù)制到獨(dú)立的變量中保存,所以雙方對元素的修改都會互相影響。而且 SubList 強(qiáng)引用了原始的 List,所以大量保存這樣的 SubList 其實(shí)也保存了大量原始的 List,從而導(dǎo)致 OOM。
  4. 分析 listIterator 方法可知,遍歷 SubList 的時候會先獲得迭代器,比較原始 ArrayList modCount 的值和 SubList 當(dāng)前 modCount 的值,如果不想等,就會拋出 ConcurrentModificationException 異常。所以上述實(shí)驗代碼,我們在獲得了 SubList 為原始 List 新增了一個元素,修改了原始 List 的 modCount,所以判等失敗拋出異常。

綜上,既然 SubList 和原始 List 會相互影響,那么避免相互影響的修復(fù)方式有兩種:

  1. 不直接使用 subList 方法返回的 SubList,而是重新使用 new ArrayList,在構(gòu)造方法傳入 SubList,來構(gòu)建一個獨(dú)立的 ArrayList:
List<Integer> subList = new ArrayList<>(list.subList(1, 4));
  1. 對于 Java 8 使用 Stream 的 skip 和 limit API 來跳過流中的元素,以及限制流中元素的個數(shù),同樣可以達(dá)到 SubList 切片的目的:
List<Integer> subList = list.stream().skip(1).limit(3).collect(Collectors.toList());


責(zé)任編輯:武曉燕 來源: 飛天小牛肉
相關(guān)推薦

2021-06-13 12:03:46

SaaS軟件即服務(wù)

2021-10-09 00:02:04

DevOps敏捷開發(fā)

2022-03-27 20:32:28

Knative容器事件模型

2021-03-25 11:24:25

爬蟲技術(shù)開發(fā)

2021-08-31 19:14:38

技術(shù)埋點(diǎn)運(yùn)營

2022-04-27 18:25:02

數(shù)據(jù)采集維度

2021-10-17 20:38:30

微服務(wù)內(nèi)存組件

2020-11-03 07:04:39

云計算公有云私有云

2021-10-12 18:31:40

流量運(yùn)營前端

2021-09-03 18:38:13

數(shù)據(jù)湖數(shù)據(jù)倉庫

2021-12-03 18:25:56

數(shù)據(jù)指標(biāo)本質(zhì)

2021-02-14 00:21:37

區(qū)塊鏈數(shù)字貨幣金融

2021-03-03 21:31:24

量化投資利潤

2021-06-29 11:21:41

數(shù)據(jù)安全網(wǎng)絡(luò)安全黑客

2022-01-05 18:27:44

數(shù)據(jù)挖掘工具

2020-11-30 08:34:44

大數(shù)據(jù)數(shù)據(jù)分析技術(shù)

2022-04-12 18:29:41

元數(shù)據(jù)系統(tǒng)架構(gòu)

2022-04-22 11:26:55

數(shù)據(jù)管理架構(gòu)

2022-07-31 20:29:28

日志系統(tǒng)

2021-01-26 10:17:48

智能語音大數(shù)據(jù)機(jī)器學(xué)習(xí)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號