欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java面試題沖刺第二十一天--JVM

 更新時間:2021年08月09日 10:29:28   作者:_陳哈哈  
這篇文章主要為大家分享了最有價(jià)值的三道關(guān)于JVM的面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下

面試題1:你遇到過哪些OOM情況,什么原因造成的?怎么解決的?

該問題主要針對你遇到的實(shí)際問題出發(fā),可以根據(jù)你實(shí)際遇到過的情況和場景,結(jié)合下面每種情況的具體原因和解決方式,整理后回答。

在這里插入圖片描述

Java heap space

當(dāng)堆內(nèi)存(Heap Space)沒有足夠空間存放新創(chuàng)建的對象時,就會拋出 java.lang.OutOfMemoryError:Javaheap space錯誤(根據(jù)實(shí)際生產(chǎn)經(jīng)驗(yàn),可以對程序日志中的 OutOfMemoryError 配置關(guān)鍵字告警,一經(jīng)發(fā)現(xiàn),立即處理)。

原因分析

Javaheap space 錯誤產(chǎn)生的常見原因可以分為以下幾類:

  • 請求創(chuàng)建一個超大對象,通常是一個大數(shù)組。
  • 超出預(yù)期的訪問量/數(shù)據(jù)量,通常是上游系統(tǒng)請求流量飆升,常見于各類促銷/秒殺活動,可以結(jié)合業(yè)務(wù)流量指標(biāo)排查是否有尖狀峰值。
  • 過度使用終結(jié)器(Finalizer),該對象沒有立即被 GC。
  • 內(nèi)存泄漏(Memory Leak),大量對象引用沒有釋放,JVM 無法對其自動回收,常見于使用了 File 等資源沒有回收。

解決方案

針對大部分情況,通常只需要通過-Xmx 參數(shù)調(diào)高 JVM 堆內(nèi)存空間即可。如果仍然沒有解決,可以參考以下情況做進(jìn)一步處理:

  • 如果是超大對象,可以檢查其合理性,比如是否一次性查詢了數(shù)據(jù)庫全部結(jié)果,而沒有做結(jié)果數(shù)限制。
  • 如果是業(yè)務(wù)峰值壓力,可以考慮添加機(jī)器資源,或者做限流降級。
  • 如果是內(nèi)存泄漏,需要找到持有的對象,修改代碼設(shè)計(jì),比如關(guān)閉沒有釋放的連接。

GC overhead limit exceeded

當(dāng) Java 進(jìn)程花費(fèi) 98% 以上的時間執(zhí)行 GC,但只恢復(fù)了不到 2% 的內(nèi)存,且該動作連續(xù)重復(fù)了 5 次,就會拋出 java.lang.OutOfMemoryError:GC overhead limit exceeded 錯誤。簡單地說,就是應(yīng)用程序已經(jīng)基本耗盡了所有可用內(nèi)存, GC 也無法回收。

此類問題的原因與解決方案跟 Javaheap space 非常類似,可以參考上條。

 Permgen space

該錯誤表示永久代(Permanent Generation)已用滿,通常是因?yàn)榧虞d的 class 數(shù)目太多或體積太大。

原因分析

永久代存儲對象主要包括以下幾類:

  • 加載/緩存到內(nèi)存中的 class 定義,包括類的名稱,字段,方法和字節(jié)碼;
  • 常量池;
  • 對象數(shù)組/類型數(shù)組所關(guān)聯(lián)的 class;
  • JIT 編譯器優(yōu)化后的 class 信息。

PermGen 的使用量與加載到內(nèi)存的 class 的數(shù)量/大小正相關(guān)。

解決方案

根據(jù) Permgen space 報(bào)錯的時機(jī),可以采用不同的解決方案,如下所示:

  • 程序啟動報(bào)錯,修改 -XX:MaxPermSize 啟動參數(shù),調(diào)大永久代空間。
  • 應(yīng)用重新部署時報(bào)錯,很可能是沒有應(yīng)用沒有重啟,導(dǎo)致加載了多份 class 信息,只需重啟 JVM 即可解決。
  • 運(yùn)行時報(bào)錯,應(yīng)用程序可能會動態(tài)創(chuàng)建大量 class,而這些 class 的生命周期很短暫,但是 JVM 默認(rèn)不會卸載 class,可以設(shè)置 -XX:+CMSClassUnloadingEnabled 和 -XX:+UseConcMarkSweepGC這兩個參數(shù)允許 JVM 卸載 class。

如果上述方法無法解決,可以通過 jmap 命令 dump 內(nèi)存對象 jmap-dump:format=b,file=dump.hprof ,然后利用 Eclipse MAT https://www.eclipse.org/mat 功能逐一分析開銷最大的 classloader 和重復(fù) class。

Metaspace

JDK 1.8 使用 Metaspace 替換了永久代(Permanent Generation),該錯誤表示 Metaspace 已被用滿,通常是因?yàn)榧虞d的 class 數(shù)目太多或體積太大。

此類問題的原因與解決方法跟 Permgenspace 非常類似,可以參考上文。需要特別注意的是調(diào)整 Metaspace 空間大小的啟動參數(shù)為-XX:MaxMetaspaceSize。

Unable to create new native thread

每個 Java 線程都需要占用一定的內(nèi)存空間,當(dāng)JVM 向底層操作系統(tǒng)請求創(chuàng)建一個新的 native 線程時,如果沒有足夠的資源分配就會報(bào)此類錯誤。

原因分析

JVM 向 OS 請求創(chuàng)建 native 線程失敗,就會拋出 Unableto createnewnativethread,常見的原因包括以下幾類:

  • 線程數(shù)超過操作系統(tǒng)最大線程數(shù) ulimit 限制;
  • 線程數(shù)超過 kernel.pid_max(只能重啟);
  • native 內(nèi)存不足;

該問題發(fā)生的常見過程主要包括以下幾步:

  • JVM 內(nèi)部的應(yīng)用程序請求創(chuàng)建一個新的 Java 線程;
  • JVM native 方法代理了該次請求,并向操作系統(tǒng)請求創(chuàng)建一個 native 線程;
  • 操作系統(tǒng)嘗試創(chuàng)建一個新的 native 線程,并為其分配內(nèi)存;
  • 如果操作系統(tǒng)的虛擬內(nèi)存已耗盡,或是受到 32 位進(jìn)程的地址空間限制,操作系統(tǒng)就會拒絕本次 native 內(nèi)存分配;
  • JVM 將拋出 java.lang.OutOfMemoryError:Unableto createnewnativethread 錯誤。

解決方案

  • 升級配置,為機(jī)器提供更多的內(nèi)存;
  • 降低 Java Heap Space 大小;
  • 修復(fù)應(yīng)用程序的線程泄漏問題;
  • 限制線程池大??;
  • 使用 -Xss 參數(shù)減少線程棧的大??;
  • 調(diào)高 OS 層面的線程最大數(shù):執(zhí)行 ulimia-a 查看最大線程數(shù)限制,使用 ulimit-u xxx 調(diào)整最大線程數(shù)限制。

Out of swap space?

該錯誤表示所有可用的虛擬內(nèi)存已被耗盡。虛擬內(nèi)存(Virtual Memory)由物理內(nèi)存(Physical Memory)和交換空間(Swap Space)兩部分組成。當(dāng)運(yùn)行時程序請求的虛擬內(nèi)存溢出時就會報(bào) Outof swap space 錯誤。

原因分析

該錯誤出現(xiàn)的常見原因包括以下幾類:

  • 地址空間不足;
  • 物理內(nèi)存已耗光;
  • 應(yīng)用程序的本地內(nèi)存泄漏(native leak),例如不斷申請本地內(nèi)存,卻不釋放。
  • 執(zhí)行 jmap-histo:live 命令,強(qiáng)制執(zhí)行 Full GC;如果幾次執(zhí)行后內(nèi)存明顯下降,則基本確認(rèn)為 Direct ByteBuffer 問題。

解決方案

根據(jù)錯誤原因可以采取如下解決方案:

  • 升級地址空間為 64 bit;
  • 使用 Arthas 檢查是否為 Inflater/Deflater 解壓縮問題,如果是,則顯式調(diào)用 end 方法。
  • Direct ByteBuffer 問題可以通過啟動參數(shù) -XX:MaxDirectMemorySize 調(diào)低閾值。
  • 升級服務(wù)器配置/隔離部署,避免爭用。

Kill process or sacrifice child

有一種內(nèi)核作業(yè)(Kernel Job)名為 Out of Memory Killer,它會在可用內(nèi)存極低的情況下“殺死”(kill)某些進(jìn)程。OOM Killer 會對所有進(jìn)程進(jìn)行打分,然后將評分較低的進(jìn)程“殺死”,具體的評分規(guī)則可以參考 Surviving the Linux OOM Killer。

不同于其他的 OOM 錯誤, Killprocessorsacrifice child 錯誤不是由 JVM 層面觸發(fā)的,而是由操作系統(tǒng)層面觸發(fā)的。

原因分析

默認(rèn)情況下,Linux 內(nèi)核允許進(jìn)程申請的內(nèi)存總量大于系統(tǒng)可用內(nèi)存,通過這種“錯峰復(fù)用”的方式可以更有效的利用系統(tǒng)資源。

然而,這種方式也會無可避免地帶來一定的“超賣”風(fēng)險(xiǎn)。例如某些進(jìn)程持續(xù)占用系統(tǒng)內(nèi)存,然后導(dǎo)致其他進(jìn)程沒有可用內(nèi)存。此時,系統(tǒng)將自動激活 OOM Killer,尋找評分低的進(jìn)程,并將其“殺死”,釋放內(nèi)存資源。

解決方案

  • 升級服務(wù)器配置/隔離部署,避免爭用。
  • OOM Killer 調(diào)優(yōu)。

Requested array size exceeds VM limit

JVM 限制了數(shù)組的最大長度,該錯誤表示程序請求創(chuàng)建的數(shù)組超過最大長度限制。

JVM 在為數(shù)組分配內(nèi)存前,會檢查要分配的數(shù)據(jù)結(jié)構(gòu)在系統(tǒng)中是否可尋址,通常為 Integer.MAX_VALUE-2。

此類問題比較罕見,通常需要檢查代碼,確認(rèn)業(yè)務(wù)是否需要創(chuàng)建如此大的數(shù)組,是否可以拆分為多個塊,分批執(zhí)行。

Direct buffer memory

Java 允許應(yīng)用程序通過 Direct ByteBuffer 直接訪問堆外內(nèi)存,許多高性能程序通過 Direct ByteBuffer 結(jié)合內(nèi)存映射文件(Memory Mapped File)實(shí)現(xiàn)高速 IO。

原因分析

Direct ByteBuffer 的默認(rèn)大小為 64 MB,一旦使用超出限制,就會拋出 Directbuffer memory 錯誤。

解決方案

  • Java 只能通過 ByteBuffer.allocateDirect 方法使用 Direct ByteBuffer,因此,可以通過 Arthas 等在線診斷工具攔截該方法進(jìn)行排查。
  • 檢查是否直接或間接使用了 NIO,如 netty,jetty 等。
  • 通過啟動參數(shù) -XX:MaxDirectMemorySize 調(diào)整 Direct ByteBuffer 的上限值。
  • 檢查 JVM 參數(shù)是否有 -XX:+DisableExplicitGC 選項(xiàng),如果有就去掉,因?yàn)樵搮?shù)會使 System.gc() 失效。
  • 檢查堆外內(nèi)存使用代碼,確認(rèn)是否存在內(nèi)存泄漏;或者通過反射調(diào)用 sun.misc.Cleaner 的 clean() 方法來主動釋放被 Direct ByteBuffer 持有的內(nèi)存空間。
  • 內(nèi)存容量確實(shí)不足,升級配置。

面試題2:說說JVM的內(nèi)存結(jié)構(gòu)?

Java 虛擬機(jī)在執(zhí)行 Java 程序的過程中會把它管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域。每個區(qū)域都有各自的作用,其中有一些會隨著虛擬機(jī)啟動而創(chuàng)建,隨著虛擬機(jī)退出而銷毀。另外一些則是與線程一一對應(yīng)的,這些與線程一一對應(yīng)的數(shù)據(jù)區(qū)域會隨著線程開始和結(jié)束而創(chuàng)建和銷毀。

  • 線程私有:程序計(jì)數(shù)器、JVM棧、本地棧
  • 線程共享:堆、方法區(qū)(永久代或元空間、代碼緩存)

在這里插入圖片描述

  • 堆內(nèi)存(Java Heap)

對于大多數(shù)應(yīng)用來說,Java堆是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動時創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實(shí)例,幾乎所有的對象實(shí)例都在這里分配內(nèi)存。

在這里插入圖片描述

Java堆是垃圾收集器管理的主要區(qū)域,因此很多時候也被稱做“GC堆”。如果從內(nèi)存回收的角度看,由于現(xiàn)在收集器基本都是采用的分代收集算法,所以Java堆中還可以細(xì)分為:新生代和老年代;再細(xì)致一點(diǎn)的有Eden空間、From Survivor空間、To Survivor空間,默認(rèn)情況下年輕代按照8:1:1的比例來分配。

  • 方法區(qū)(Method Area)

與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。雖然Java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個邏輯部分,但是它卻有一個別名叫做Non-Heap(非堆),目的應(yīng)該是與Java堆區(qū)分開來。

對于習(xí)慣在HotSpot虛擬機(jī)上開發(fā)和部署程序的開發(fā)者來說,很多人愿意把方法區(qū)稱為“永久代”(Permanent Generation),本質(zhì)上兩者并不等價(jià),僅僅是因?yàn)镠otSpot虛擬機(jī)的設(shè)計(jì)團(tuán)隊(duì)選擇把GC分代收集擴(kuò)展至方法區(qū),或者說使用永久代來實(shí)現(xiàn)方法區(qū)而已。

  • 程序計(jì)數(shù)器(Program Counter Register)

程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。在虛擬機(jī)的概念模型里(僅是概念模型,各種虛擬機(jī)可能會通過一些更高效的方式去實(shí)現(xiàn)),字節(jié)碼解釋器工作時就是通過改變這個計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個計(jì)數(shù)器來完成。

此內(nèi)存區(qū)域是唯一一個在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。

  • JVM棧(JVM Stacks)

與程序計(jì)數(shù)器一樣,Java虛擬機(jī)棧也是線程私有的,它的生命周期與線程相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法被執(zhí)行的時候都會同時創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作棧、動態(tài)鏈接、方法出口等信息。每一個方法被調(diào)用直至執(zhí)行完成的過程,就對應(yīng)著一個棧幀在虛擬機(jī)棧中從入棧到出棧的過程。

在這里插入圖片描述

局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型、對象引用和returnAddress類型(指向了一條字節(jié)碼指令的地址)。

在Java虛擬機(jī)規(guī)范中,對這個區(qū)域規(guī)定了兩種異常狀況:

  • 如果線程請求的棧深度大于虛擬機(jī)所允許的深度,將拋出StackOverflowError異常;
  • 如果虛擬機(jī)棧可以動態(tài)擴(kuò)展,當(dāng)擴(kuò)展時無法申請到足夠的內(nèi)存時會拋出OutOfMemoryError異常。
  • 本地方法棧(Native Method Stacks)

本地方法棧與虛擬機(jī)棧所發(fā)揮的作用是非常相似的,其區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則是為虛擬機(jī)使用到的Native方法服務(wù)。

虛擬機(jī)規(guī)范中對本地方法棧中的方法使用的語言、使用方式與數(shù)據(jù)結(jié)構(gòu)并沒有強(qiáng)制規(guī)定,因此具體的虛擬機(jī)可以自由實(shí)現(xiàn)它。甚至有的虛擬機(jī)(譬如Sun HotSpot虛擬機(jī))直接就把本地方法棧和虛擬機(jī)棧合二為一。與虛擬機(jī)棧一樣,本地方法棧區(qū)域也會拋出StackOverflowError和OutOfMemoryError異常。

面試題3:說一下new一個對象的過程是什么樣的?

當(dāng)虛擬機(jī)遇見new關(guān)鍵字時候,實(shí)現(xiàn)判斷當(dāng)前類是否已經(jīng)加載,如果類沒有加載,首先執(zhí)行類的加載機(jī)制,加載完成后再為對象分配空間、初始化等。

在這里插入圖片描述

首先校驗(yàn)當(dāng)前類是否被加載,如果沒有加載,執(zhí)行類加載機(jī)制

  • 加載:就是從字節(jié)碼加載成二進(jìn)制流的過程
  • 驗(yàn)證:當(dāng)然加載完成之后,當(dāng)然需要校驗(yàn)Class文件是否符合虛擬機(jī)規(guī)范,跟我們接口請求一樣,第一件事情當(dāng)然是先做個參數(shù)校驗(yàn)了
  • 準(zhǔn)備:為靜態(tài)變量、常量賦默認(rèn)值
  • 解析:把常量池中符號引用(以符號描述引用的目標(biāo))替換為直接引用(指向目標(biāo)的指針或者句柄等)的過程
  • 初始化:執(zhí)行static代碼塊(cinit)進(jìn)行初始化,如果存在父類,先對父類進(jìn)行初始化

tips:靜態(tài)代碼塊是絕對線程安全的,只能隱式被java虛擬機(jī)在類加載過程中初始化調(diào)用!

當(dāng)類加載完成之后,緊接著就是對象分配內(nèi)存空間和初始化的過程

  • 首先為對象分配合適大小的內(nèi)存空間
  • 接著為實(shí)例變量賦默認(rèn)值
  • 設(shè)置對象的頭信息,對象hash碼、GC分代年齡、元數(shù)據(jù)信息等
  • 執(zhí)行構(gòu)造函數(shù)(init)初始化 每日小結(jié)

總結(jié)

本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • java?socket實(shí)現(xiàn)局域網(wǎng)聊天

    java?socket實(shí)現(xiàn)局域網(wǎng)聊天

    這篇文章主要為大家詳細(xì)介紹了java?socket實(shí)現(xiàn)局域網(wǎng)聊天,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Java Reference源碼解析

    Java Reference源碼解析

    這篇文章主要為大家詳細(xì)解析了Java Reference源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • IDEA之如何關(guān)閉/開啟引用提示Usages

    IDEA之如何關(guān)閉/開啟引用提示Usages

    這篇文章主要介紹了IDEA之如何關(guān)閉/開啟引用提示Usages問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 淺析JDK12的五大重要新特性(推薦)

    淺析JDK12的五大重要新特性(推薦)

    這篇文章主要介紹了JDK12的五大重要新特性,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • 關(guān)于文件上傳MultipartBody的使用方法

    關(guān)于文件上傳MultipartBody的使用方法

    這篇文章主要介紹了關(guān)于文件上傳MultipartBody的使用方法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 淺談spring-boot的單元測試中,@Before不被執(zhí)行的原因

    淺談spring-boot的單元測試中,@Before不被執(zhí)行的原因

    這篇文章主要介紹了淺談spring-boot的單元測試中,@Before不被執(zhí)行的原因,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • 淺談Java自定義注解和運(yùn)行時靠反射獲取注解

    淺談Java自定義注解和運(yùn)行時靠反射獲取注解

    下面小編就為大家?guī)硪黄獪\談Java自定義注解和運(yùn)行時靠反射獲取注解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-11-11
  • SpringBoot項(xiàng)目打包部署到Tomcat的操作流程

    SpringBoot項(xiàng)目打包部署到Tomcat的操作流程

    在最近一個項(xiàng)目中,維護(hù)行里一個年代較為久遠(yuǎn)的單體項(xiàng)目,需要將項(xiàng)目打包放到的tomcat服務(wù)器下運(yùn)行,所以本文就給大家介紹一下SpringBoot項(xiàng)目打包部署到Tomcat的流程步驟,需要的朋友可以參考下
    2023-08-08
  • Java線程優(yōu)先級變量及功能

    Java線程優(yōu)先級變量及功能

    這篇文章主要介紹了Java線程優(yōu)先級變量及功能,關(guān)于優(yōu)先級的問可能有兩個或更多線程被分配了相同的優(yōu)先級,那么它們的執(zhí)行取決于操作系統(tǒng),更多相關(guān)介紹,需要的小伙伴可以參考一下
    2022-06-06
  • Java開發(fā)者必備10大數(shù)據(jù)工具和框架

    Java開發(fā)者必備10大數(shù)據(jù)工具和框架

    這篇文章主要為大家詳細(xì)介紹了Java開發(fā)者必備10大數(shù)據(jù)工具和框架,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06

最新評論