Java-List集合堆內存溢出
- 情況一
- 情況二
- 對照分析
- 對照規定堆內存
情況一
往List<Object>的集合中不斷插入元素,集合底層的數組會不斷擴容,從0 -> 10 -> 10 + 10>>1…。最終出現堆內存溢出,是在擴容數組大小的時候。這里的過程會比下面之間add(“1”)出現內存溢出的時間要長,但是容量比其要小。
說明Object的對象創建會占用一定的空間;數組里面存放執行堆的內存地址也是需要占用空間的;GC掃描這些對象所花的時間也長(-XX:+PrintGCDetails -Xms4g -Xmx4g -XX:NewRatio=4,可以看到不斷有GC日志打印,加上jstat -gc pid查詢GC的耗時,以及T線程出現內存溢出時,main線程出現異常等待的時間就是最近一次add觸發GC的時間)。
public static void main(String[] args) {List<Object> list = new ArrayList<>();Thread thread = new Thread(() -> {synchronized (list) {while (true) {list.add(new Object());}}}, "T ");thread.start();try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}Iterator<Object> iterator = list.iterator();while (iterator.hasNext()){if (!thread.isAlive()){System.out.println(list.size());break;}}list.add(new Object());
}
情況二
public static void main(String[] args) {List<Object> list = new ArrayList<>();Thread thread = new Thread(() -> {synchronized (list) {while (true) {list.add("1");}}}, "T ");thread.start();try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}Iterator<Object> iterator = list.iterator();while (iterator.hasNext()){if (!thread.isAlive()){System.out.println(list.size());break;}}list.add("1");
}
對照分析
JVM啟動參數:-Xms4g -Xmx4g -XX:NewRatio=4
public static void main(String[] args) {long start = System.currentTimeMillis();List<Object> list = new ArrayList<>();Thread thread = new Thread(() -> {synchronized (list) {while (true) {list.add(new Object());}}}, "T");thread.start();while (true) {if (!thread.isAlive()) {System.out.println(System.currentTimeMillis() - start);System.out.println(list.size());break;}}list.add(new Object());
}
在指定大小足夠大的情況下時,同樣的啟動參數,為什么GC次數少但是耗時更長?
但是用另外一臺電腦,后者速度會更快一點,為什么?
程序運行久,在后面真正耗時的是GC的時間,耗時75秒,總GC將近74秒。
對照規定堆內存
-XX:+PrintGCDetails -Xms1g -Xmx1g -XX:NewRatio=10 -XX:-UseAdaptiveSizePolicy