問題來源
一次生產事故,由于一次性從數據庫查詢過多數據導致線程 OOM:Java heap space 異常(千萬級表,JVM堆內存2G),但是在線程OOM發生時,java進程卻沒有立即掛掉。
##OOM與異常
說到底OutOfMemoryError也只是一個java中的異常而已,屬于Error一系非檢查異常:
ObjectThrowableErrorVirtualMachineErrorOutOfMemoryError
堆內存不夠與異常的關系
線程發生OOM Java heap space,首先是堆空間不夠了,然后再由jvm在申請分配空間的方法調用上拋出OOM異常。
對于線程,它會像處理普通異常一樣,處理OutOfMemoryError。
實例
package org.example;import com.sun.javafx.scene.control.skin.TableHeaderRow;import java.util.ArrayList;//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {public static void main(String[] args) throws InterruptedException{//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text// to see how IntelliJ IDEA suggests fixing it.System.out.printf("Hello and welcome!");System.out.println("JVM從OS獲取的 最大 內存" + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "M");System.out.println("JVM從OS獲取的 當前 內存" + Runtime.getRuntime().totalMemory() / 1024 / 1024 + "M");System.out.println("JVM從OS獲取的 但空閑 內存" + Runtime.getRuntime().freeMemory() / 1024 / 1024 + "M");// 創建線程new Thread(() -> {ArrayList<byte[]> bytes = new ArrayList<>();for (int i = 0; i < 2000; i++) {byte[] bytes1 = new byte[3 * 1024 * 1024];bytes.add(bytes1);System.out.println(Thread.currentThread().getName()+":JVM從OS獲取的 當前 內存" + Runtime.getRuntime().totalMemory() / 1024 / 1024 + "M");}}).start();// 創建線程new Thread(() -> {ArrayList<byte[]> bytes = new ArrayList<>();for (int i = 0; i < 2000; i++) {byte[] bytes1 = new byte[3 * 1024 * 1024];bytes.add(bytes1);System.out.println(Thread.currentThread().getName()+":JVM從OS獲取的 當前 內存" + Runtime.getRuntime().totalMemory() / 1024 / 1024 + "M");}}).start();while (true){System.out.println(Thread.currentThread().getName() + " continuing...");Thread.sleep(1000L);}}
}
結果:
##本機64位,16G內存,默認最大堆3600M
JVM從OS獲取的 最大 內存3600M
JVM從OS獲取的 當前 內存243M
JVM從OS獲取的 但空閑 內存237M
main continuing...##2個線程分別開始創建byte數組,占用內存
Thread-0:JVM從OS獲取的 當前 內存307M
Thread-1:JVM從OS獲取的 當前 內存307M
Thread-1:JVM從OS獲取的 當前 內存307M
Thread-0:JVM從OS獲取的 當前 內存307M##JVM增長到極限,Thread-1先報OOM
Thread-0:JVM從OS獲取的 當前 內存3366M
Thread-0:JVM從OS獲取的 當前 內存3366M
Exception in thread "Thread-1" java.lang.OutOfMemoryError: Java heap spaceat org.example.Main.lambda$main$1(Main.java:39)at org.example.Main$$Lambda$2/1828972342.run(Unknown Source)at java.lang.Thread.run(Thread.java:750)
Thread-0:JVM從OS獲取的 當前 內存3366M
Thread-0:JVM從OS獲取的 當前 內存3366M#Thread-1失敗終止后,起引用的對象也就可以GC了,Thread-0又獲取了好多內存,直到OOM
Thread-0:JVM從OS獲取的 當前 內存3616M
Thread-0:JVM從OS獲取的 當前 內存3616M
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap spaceat org.example.Main.lambda$main$0(Main.java:29)at org.example.Main$$Lambda$1/1989780873.run(Unknown Source)at java.lang.Thread.run(Thread.java:750)
main continuing...
main continuing...
main continuing...
結論
1、OOM是在線程上發生的,會被當做一般異常處理,不會導致JVM的退出;
2、多線程公用JVM,一個線程終止GC后,內存重新給其他線程分配。