oom會不會導(dǎo)致jvm宕機

在實際生產(chǎn)中偶爾會遇到oom,導(dǎo)致服務(wù)器的宕機,如果使用了堆外內(nèi)存也有可能導(dǎo)致k8s的pod宕機這是另一個問題了,發(fā)生oom的原因就是內(nèi)存沒有足夠的空間分配對象,在oom之前會發(fā)生多次的fullgc,依然不能釋放空間則就會使jvm停止,
那發(fā)生oom一定會導(dǎo)致服務(wù)器宕機嗎,不是的,oom其實也是一個異常,類型是OutOfMemoryError,是可以trycatch的,如果發(fā)生異常其實就是退出當(dāng)前線程.
spring的main線程負(fù)責(zé)啟動,初始化容器,監(jiān)聽器等功能,真正處理業(yè)務(wù)都是非main線程,比如tomcat創(chuàng)建的線程處理業(yè)務(wù),當(dāng)發(fā)生oom的時候,其實只是拋出一個異常,退出當(dāng)前線程,如果是main線程那可能jvm直接退出了
如果大對象變量是線程內(nèi)部變量,當(dāng)線程退出的時候,大對象會被GC掉的,并不會導(dǎo)致整個jvm宕機,如果大對象是線程共享的,這個就很可能導(dǎo)致jvm宕機,因為大對象不回因為線程的退出而被GC掉,GCROOT可達(dá)
示例

public class OomTest {
    public static List<byte[]> list = new ArrayList<>();

    @RequestMapping("hello")
    public String oom(@RequestBody Map<String, String> map) throws InterruptedException{
        return null != map ? map.get("nihao") : "參數(shù)為null";
    }

    byte[] bytes = new byte[new Random().nextInt(1024*1024)];

    public static void main(String[] args) throws InterruptedException{
//如果list放在主線程中,是不會GC掉的,因為被main線程棧引用
//List<OomTest> list = new ArrayList<>();
        new Thread(()->{
//線程里的對象,當(dāng)線程因為oom退出的時候,list會被gc掉
            List<OomTest> list = new ArrayList<>();
            while(true){
//這里trycatch是可以被捕獲的 OutOfMemoryError異常,如果在catch中將list.clear(),當(dāng)前線程也不回退出
                list.add(new OomTest());
                try{
                    Thread.sleep(100);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("非主線程");
            }
        }).start();
//主線程依然會運行
        while(true){
            try{
                Thread.sleep(30);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("主線程");
        }
    }
}

實際生產(chǎn)中基本都是成員變量導(dǎo)致不能被回收,從而導(dǎo)致jvm發(fā)生宕機.


image.png

image.png

觀察jvisualvm工具,中間顏色較深的是老年代,第一張圖是還沒有發(fā)生oom,老年代還呈現(xiàn)上升趨勢,第二張圖是發(fā)生了oom,當(dāng)線程退出的時候,老年代的文件會被gc掉,因為gcroot不可達(dá),文件大部分被回收了,如果list放在線程外面就不回被回收,因為被main線程引用.
什么是線程退出呢,其實就是線程執(zhí)行結(jié)束了,上面提到了當(dāng)線程發(fā)生了異常被trycatch,線程就執(zhí)行結(jié)束了,因為catch里方法執(zhí)行完,下面就沒有要執(zhí)行的方法了,就退出了唄.線程池的線程是在while(true)中,獲取阻塞隊列的任務(wù)(runnable),如果沒有任務(wù)就阻塞等待任務(wù)向隊列offer,如果線程數(shù)大于最大線程數(shù)量,退出while循環(huán),線程就結(jié)束了,線程對于java就是一個普通的對象,是會被回收的,真正調(diào)用start方法的時候,才會在內(nèi)核創(chuàng)建與之關(guān)聯(lián)的線程,由cup去執(zhí)行,start會調(diào)用start0方法,本地方法.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、從編碼到執(zhí)行 解釋執(zhí)行和編譯執(zhí)行是可以混合的,執(zhí)行次數(shù)多的代碼,會進(jìn)行 JIT 的編譯,交由操作系統(tǒng)直接執(zhí)行。...
    ArthurHC閱讀 547評論 0 2
  • 歡迎關(guān)注公眾號“Tim在路上” 1.聽說你對JVM有點研究,講一講JVM的內(nèi)存模型吧(我說虛擬機棧,本地方法棧,程...
    Tim在路上閱讀 3,972評論 4 91
  • 1.1 并行和并發(fā)有什么區(qū)別? 并發(fā)(concurrency)和并行(parallellism)是: 并行是指兩個...
    雪飄千里閱讀 922評論 0 9
  • 1.java基礎(chǔ) 1.1 說一說java有哪些集合 答:分層次記憶:第一層:Collection;第二層:List...
    java_飛閱讀 2,535評論 5 40
  • 緩存 memcache的分布式原理 memcached 雖然稱為 “ 分布式 ” 緩存服務(wù)器,但服務(wù)器端并沒有 “...
    元氣軟件閱讀 465評論 0 0

友情鏈接更多精彩內(nèi)容