Java List的SubList使用問題

来源:https://www.cnblogs.com/Brake/archive/2020/04/19/12732929.html
-Advertisement-
Play Games

一、Sublist導致OOM 代碼 @Slf4j public class SubListDemo { public static void subListOOM() { List<List<Integer>> data = new ArrayList<>(); for (int i = 0; i ...


一、Sublist導致OOM

代碼

@Slf4j
public class SubListDemo {

    public static void subListOOM() {
        List<List<Integer>> data = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            List<Integer> rawList = IntStream.rangeClosed(1, 100000).boxed().collect(Collectors.toList());//構建一個100000個元素的list
            data.add(rawList.subList(0, 1));
        }
        log.info("data.size(): " +data.size());
    }
}

OOM

Exception in thread "File Watcher" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.io.File.listFiles(File.java:1212)
    at org.springframework.boot.devtools.filewatch.FolderSnapshot.collectFiles(FolderSnapshot.java:63)
    at org.springframework.boot.devtools.filewatch.FolderSnapshot.collectFiles(FolderSnapshot.java:67)
    at org.springframework.boot.devtools.filewatch.FolderSnapshot.collectFiles(FolderSnapshot.java:67)
    at org.springframework.boot.devtools.filewatch.FolderSnapshot.collectFiles(FolderSnapshot.java:67)
    at org.springframework.boot.devtools.filewatch.FolderSnapshot.<init>(FolderSnapshot.java:58)
    at org.springframework.boot.devtools.filewatch.FileSystemWatcher$Watcher.getCurrentSnapshots(FileSystemWatcher.java:277)
    at org.springframework.boot.devtools.filewatch.FileSystemWatcher$Watcher.scan(FileSystemWatcher.java:251)
    at org.springframework.boot.devtools.filewatch.FileSystemWatcher$Watcher.run(FileSystemWatcher.java:236)
    at java.lang.Thread.run(Thread.java:748)
Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.lang.Integer.valueOf(Integer.java:832)
    at java.util.stream.IntPipeline$$Lambda$492/1604894031.apply(Unknown Source)
    at java.util.stream.IntPipeline$4$1.accept(IntPipeline.java:250)
    at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
    at java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:693)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at com.example.newdemo.SubListDemo.subListOOM(SubListDemo.java:17)
    at com.example.newdemo.NewdemoApplication.main(NewdemoApplication.java:13)

分析

出現 OOM 的原因是,迴圈中的 1000 個具有 10 萬個元素的 List 始終得不到回收,因為它始終被 subList 方法返回的 List 強引用。

    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

 

parent 欄位就是原始的 List。SubList沒有copy一份自己的數據,而是完整的保留了原始的list。 SubList 是原始 List 的視圖,並不是獨立的 List, SubList 強引用了原始的 List,所以大量保存這樣的 SubList 會導致 OOM。

解決

不直接使用 subList 方法返回的 SubList,而是重新使用 new ArrayList,在構造方法傳入 SubList,來構建一個獨立的 ArrayList。sublist直接釋放-》原始的list也被釋放。

   public static void subListWithoutOOM() {
        List<List<Integer>> data = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            List<Integer> rawList = IntStream.rangeClosed(1, 100000).boxed().collect(Collectors.toList());//構建一個100000個元素的list
            data.add(new ArrayList<>(rawList.subList(0, 1)));
        }
        log.info("data.size(): " +data.size());
    }

另外一個例子

   public static void removeSubList() {
        List<Integer> rawList = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList());
        List<Integer> subList = rawList.subList(0, 3);
        subList.remove(0);
        rawList.forEach(System.out::print);
    }
2345678910

可以看到,移除sublist的元素後,直接影響到了原始list。

二、修改原始列表後SubList迴圈報錯

測試代碼

 public static void addItemToOriginalList() {
        List<Integer> rawList = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList());
        List<Integer> subList = rawList.subList(0, 3);

        rawList.add(11);
        try {
            subList.forEach(System.out::print);
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
java.util.ConcurrentModificationException
    at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239)
    at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099)
    at java.util.AbstractList.listIterator(AbstractList.java:299)
    at java.util.ArrayList$SubList.iterator(ArrayList.java:1095)
    at java.lang.Iterable.forEach(Iterable.java:74)
    at com.example.newdemo.SubListDemo.addItemToOriginalList(SubListDemo.java:46)
    at com.example.newdemo.NewdemoApplication.main(NewdemoApplication.java:13)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)

分析

modCount 的欄位,表示結構性修改的次數--影響list size修改測次數,add肯定影響size,導致modCount加1.但是sublist的modCount沒有變,所有拋出了異常。

代碼

 private void checkForComodification() {
            if (ArrayList.this.modCount != this.modCount)
                throw new ConcurrentModificationException();
        }

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 作者: "DeppWang" 、 "原文地址" 我通過實現一個簡易的 Spring IoC 容器,算是入門了 Spring 框架。本文是對實現過程的一個總結提煉, 需要配合源碼閱讀 , "源碼地址" 。 結合本文和源碼,你應該可以學到:Spring 的原理和 Spring Boot 的原理。 Spr ...
  • 本篇主要講解如何將golang的服務部署到kubernetes集群里,附帶相關的golang的demo和yml文件內容。純新手入門方式,生產服務需要完整的CI/CD流程來支持。 golang服務代碼 這裡簡單寫一個k8s-demo的服務,提供兩個介面ping和version介面,main.go文件代 ...
  • 案例故事:有一天測試經理髮給我一個Bug列表, 這些Bug都是被開發打回來的無效(WithDrawn)Bug, 就是開發不認為是bug, 測試經理覺得很有必要去分析下, 為什麼這麼多無效bug,到底是什麼原因? 測試經理想知道,是測試提Bug太隨意,還是無此需求,還是開發bug流轉流程不對,還是其他 ...
  • 前言: Python可謂是現在很多人正在學或者想學的一個腳本語言了,提到學習自然就少不了拿項目練手,可是一般的項目根本提不起興趣嘛,這10個項目可是非常有趣的,不信你看看。 【Python 圖片轉字元畫】 用 50 行 Python 代碼完成圖片轉字元畫小工具。通過實驗將學習到 Linux 命令行操 ...
  • 給定四種水果,分別是蘋果(apple)、梨(pear)、桔子(orange)、葡萄(grape),單價分別對應為3.00元/公斤、2.50元/公斤、4.10元/公斤、10.20元/公斤。首先在屏幕上顯示以下菜單:[1] apple[2] pear[3] orange[4] grape[0] exit... ...
  • 一、全局修改 打開即可修改 二、針對當前項目修改 ...
  • 一、I/O操作概述 I/O概述: I/O在電腦中時指Input/Output,也就是Stream的輸入與輸出。我們通常說的輸入與輸出其實在操作系統中都是相對於記憶體而言的,InputStream(輸入流)是指數據從外部(網路、鍵盤、I/O設備)流進記憶體,OutputStream正好與之相反,數據從內 ...
  • 本題要求編寫程式,輸出菲波那契(Fibonacci)數列的前N項,每行輸出5個,題目保證輸出結果在長整型範圍內。Fibonacci數列就是滿足任一項數字是前兩項的和(最開始兩項均定義為1)的數列,例如:1,1,2,3,5,8,13,...。輸入格式:輸入在一行中給出一個整數N(1≤N≤46)。輸出格... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...