最近剛做到一個內存分頁的需求,自測了幾次就 OOM 了,找了半天原因,終于把這個坑填上來,下面整理一下發出來,各位小伙伴引以為鑒。
我們經常會使用 List.subList 方法對 List 進行切片,比如取前十個元素出來用,但是和 Arrays.asList 的問題類似(具體文章可以看 慎用 ArrayList,全是坑!),List.subList 返回的子 List 不是一個全新地址的 ArrayList,這個子 List 會和原始 List 相互影響。
如果不注意,很可能會因此產生 OOM 問題。
話不多說,先復現問題。如下代碼所示,定義一個名為 data 的靜態 List 用來存放 List<Integer> 類型,循環 1000 次,每次都從一個具有 100 萬個 Integer 的 List 中(即代碼中的 rawList),使用 subList 方法獲得一個只包含一個數字的子 List,并把這個子 List 加入 data 變量:
圖片
看起來,這個 data 變量里面最終保存的只是 1000 個具有 1 個元素的 List 而已,并不會出現什么問題啊。
但是,代碼在運行到一段時間后,可以看到在我的機器上是第 159 次循環后發生了 OOM:
圖片
出現 OOM 的原因是,循環中的 1000 個具有 100 萬個元素的 List 始終得不到回收,因為它始終被 subList 方法返回的 List 強引用。
subList 返回的子 List 為啥會強引用原始的 List?再來做個實驗看下:
首先初始化一個包含數字 1 到 10 的 ArrayList,然后通過調用 subList 方法取出 2、3、4,隨后刪除這個 SubList 中的元素數字 3。可以看到原始 List 中數字 3 被刪除了,說明刪除子 List 中的元素影響到了原始 List:
圖片
圖片
繼續看,我們為原始的 ArrayList 增加一個元素數字 0,然后遍歷 SubList 輸出所有元素。代碼運行后報錯 java.util.ConcurrentModificationException:
圖片
圖片
分析下 ArrayList 的源碼,看看為什么會是這樣:
圖片
綜上,既然 SubList 和原始 List 會相互影響,那么避免相互影響的修復方式有兩種:
List<Integer> subList = new ArrayList<>(list.subList(1, 4));
List<Integer> subList = list.stream().skip(1).limit(3).collect(Collectors.toList());
本文鏈接:http://www.www897cc.com/showinfo-26-76481-0.html終于明白為啥面試老是有人問 SubList 了,原來這玩意會 OOM!
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com