一直往ArrayList數組里面放object對象實例會報OOM嗎?
1. 問題描述
一直往ArrayList數組里面放object對象實例會報OOM嗎?
問題結論沒有看到具體的代碼,說會或者不會都是耍流氓。討論這個問題,始終要以代碼和一定的前提條件為基礎進行,否則將無任何意義。就我個人理解,會報OOM的情況存在,不會報OOM的情況同樣存在。2. ArrayList數組里面放object對象的幾種示例2.1 ArrayList數組的說明ArrayList底層采用數組來存儲數據,查找速度快,畢竟直接使用數組下標進行數據的查找。這里有一點特別重要其內部的數據存儲結構為數組。數組:數組是一種線性表數據結構,它是一組連續的內存空間。ArrayList的默認容量為10,超過10時,會進行擴容:int newCapacity = oldCapacity + (oldCapacity >> 1);相當于擴大為原來的1.5倍。2.2 第一種:ArrayList里面放object對象實例不會報OOM的示例public class OomTest { public static void main(String[] args) throws InterruptedException { int count=1; for(;;){ List<Oom2Test> list=new ArrayList<Oom2Test>(); System.out.println(count++); list.add(new OomTest()); } }}你這段代碼里,列表list是個很典型的塊變量,塊變量的作用范圍,只在聲明變量時變量所在的代碼塊內,而代碼塊的劃分范圍一般都是花括號"{}"。即list變量是在for語句的一對花括號內,因此list這個變量的作用域,就只在for循環這塊代碼里,每次循環的時候,判斷是否繼續下一次的條件的時候,已經走出了這個代碼塊,所以list這個列表變量這時候已經失去引用了。即當每次循環完成時,會執行gc,循環中的變量會被回收,從而不會導致OOM。
2.3 第二種:ArrayList里面放object對象實例會報OOM的示例public class OomTest { private byte[] buffers=new byte[1024 * 1024]; public static void main(String[] args) throws InterruptedException { List<OomTest> list=new ArrayList<OomTest>(); int count=1; for(;;){ System.out.println(count++); list.add(new OomTest()); } }}你這段代碼里,列表list是個很典型的局部變量,for循環代碼塊循環進行對象創建(對象中存在占用1M內存的成員變量)并追加到list中,list中的引用始終存在,無法進行gc,從而引發OOM。
當最大堆和初始堆均設置為2G(-Xms2048M -Xmx2048M)時,for循環執行1877次(count=1877)觸發OOM。
# 代碼塊輸出結果
1877
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.wei.you.simple.thread.OomTest.<init>(OomTest.java:18)
at com.wei.you.simple.thread.OomTest.main(OomTest.java:37)
2.4 第三種:ArrayList里面放object對象實例在有限時間內未觸發OOM的示例public class OomTest { public static void main(String[] args) throws InterruptedException { List<OomTest> list=new ArrayList<OomTest>(); int count=1; for(;;){ System.out.println(count++); list.add(new OomTest()); } }}在這段代碼中,基本上和第二種示例一致,唯一的區別在于該對象中無占用1M內存的成員變量。理論上,該引用一直存在,對象無法進行gc,會報出OOM。但實際上,將最大堆和初始堆均設置為2G(-Xms2048M -Xmx2048M),運行近30分鐘,當for循環執行87984747(count=87984747)時,仍未觸發OOM# 代碼塊輸出結果87984747..........# 同時該gc日志如下2021-07-01T20:52:03.715+0800: 2680.631: [Full GC (Ergonomics) [PSYoungGen: 493567K->493567K(599040K)] [ParOldGen: 1293103K->1293101K(1398272K)] 1786671K->1786669K(1997312K), [Metaspace: 3908K->3908K(1056768K)], 7.9257904 secs] [Times: user=25.17 sys=0.14, real=7.93 secs]2021-07-01T20:52:11.641+0800: 2688.557: [Full GC (Ergonomics) [PSYoungGen: 493567K->493567K(599040K)] [ParOldGen: 1293103K->1293101K(1398272K)] 1786671K->1786669K(1997312K), [Metaspace: 3908K->3908K(1056768K)], 8.0861351 secs] [Times: user=24.50 sys=0.16, real=8.09 secs]
2021-07-01T20:52:19.727+0800: 2696.643: [Full GC (Ergonomics) [PSYoungGen: 493567K->493567K(599040K)] [ParOldGen: 1293103K->1293102K(1398272K)] 1786671K->1786670K(1997312K), [Metaspace: 3908K->3908K(1056768K)], 8.0613165 secs] [Times: user=25.22 sys=0.17, real=8.06 secs]
2021-07-01T20:52:27.789+0800: 2704.705: [Full GC (Ergonomics) [PSYoungGen: 493567K->493567K(599040K)] [ParOldGen: 1293104K->1293102K(1398272K)] 1786672K->1786670K(1997312K), [Metaspace: 3908K->3908K(1056768K)], 8.0671036 secs] [Times: user=24.89 sys=0.14, real=8.07 secs]
3. 小結上述,簡單的對一直往ArrayList數組里面放object對象實例是否會報OOM做了簡單的實驗和對比,總結如下list的作用域影響到OOM是否觸發,當list和對象在同一作用域時,應該不會觸發OOM,即第一種示例中的list和new OomTest()同屬于塊變量,不觸發OOM對象實例占用內存大小影響OOM觸發的進度,占用內存較大的對象實例更容易觸發OOM,即第二種示例中與第三種示例的對比情況。作者:夕陽雨晴,歡迎關注我的頭條號:偶爾美文,主流Java,為你講述不一樣的碼農生活。