溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JVM面試真題有哪些

發(fā)布時(shí)間:2021-12-31 14:20:40 來源:億速云 閱讀:118 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“JVM面試真題有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“JVM面試真題有哪些”吧!

第一題:JVM內(nèi)存相關(guān)(百度)

問:JVM內(nèi)存模型了解嗎,簡(jiǎn)單說下

答:

因?yàn)檫@塊內(nèi)容太多了,許多小伙伴可能記不住這么多,所以下面的答案分為簡(jiǎn)答和精答

JVM 運(yùn)行時(shí)內(nèi)存共分為程序計(jì)數(shù)器,Java虛擬機(jī)棧,本地方法棧,堆,方法區(qū)五個(gè)部分:

JVM面試真題有哪些

jdk 1.8 同 jdk 1.7 比,最大的差別就是:元數(shù)據(jù)區(qū)取代了永久代。元空間的本質(zhì)和永久代類似,都是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn)。不過元空間與永久代之間最大的區(qū)別在于:元數(shù)據(jù)空間并不在虛擬機(jī)中,而是使用本地內(nèi)存

第二題:類加載相關(guān)(新浪微博)

問:jvm加載類的過程主要有哪些,具體怎么加載?

答:

簡(jiǎn)答:類加載過程即是指JVM虛擬機(jī)把.class文件中類信息加載進(jìn)內(nèi)存,并進(jìn)行解析生成對(duì)應(yīng)的class對(duì)象的過程。分為五個(gè)步驟:加載 -> 驗(yàn)證 -> 準(zhǔn)備 -> 解析 -> 初始化。加載:將外部的 .class 文件加載到Java虛擬機(jī)中;驗(yàn)證:確保加載進(jìn)來的 calss 文件包含的額信息符合 Java 虛擬機(jī)的要求;準(zhǔn)備:為類變量分配內(nèi)存,設(shè)置類變量的初始值;解析:將常量池內(nèi)的符號(hào)引用 轉(zhuǎn)為 直接引用;初始化:初始化類變量和靜態(tài)代碼塊。

精答前方預(yù)警,內(nèi)容較長(zhǎng),做好準(zhǔn)備!

一個(gè)Java文件從編碼完成到最終執(zhí)行,一般主要包括兩個(gè)過程:編譯、運(yùn)行

  • 編譯:即把我們寫好的java文件,通過javac命令編譯成字節(jié)碼,也就是我們常說的.class文件。

  • 運(yùn)行:則是把編譯生成的.class文件交給Java虛擬機(jī)(JVM)執(zhí)行。

而我們所說的類加載過程即是指JVM虛擬機(jī)把.class文件中類信息加載進(jìn)內(nèi)存,并進(jìn)行解析生成對(duì)應(yīng)的class對(duì)象的過程。

  • 類加載過程

舉個(gè)簡(jiǎn)單的例子來說,JVM在執(zhí)行某段代碼時(shí),遇到了class A, 然而此時(shí)內(nèi)存中并沒有class A的相關(guān)信息,于是JVM就會(huì)到相應(yīng)的class文件中去尋找class A的類信息,并加載進(jìn)內(nèi)存中,這就是我們所說的類加載過程。
由此可見,JVM不是一開始就把所有的類都加載進(jìn)內(nèi)存中,而是只有第一次遇到某個(gè)需要運(yùn)行的類時(shí)才會(huì)加載,且只加載一次。

  • 類加載

類加載的過程主要分為三個(gè)部分:加載、鏈接、初始化。

而鏈接又可以細(xì)分為三個(gè)小部分:驗(yàn)證、準(zhǔn)備、解析。

  • 加載

簡(jiǎn)單來說,加載指的是把class字節(jié)碼文件從各個(gè)來源通過類加載器裝載入內(nèi)存中。

這里有兩個(gè)重點(diǎn):

字節(jié)碼來源:一般的加載來源包括從本地路徑下編譯生成的.class文件,從jar包中的.class文件,從遠(yuǎn)程網(wǎng)絡(luò),以及動(dòng)態(tài)代理實(shí)時(shí)編譯

類加載器:一般包括啟動(dòng)類加載器,擴(kuò)展類加載器,應(yīng)用類加載器,以及用戶的自定義類加載器。

注:為什么會(huì)有自定義類加載器?
一方面是由于java代碼很容易被反編譯,如果需要對(duì)自己的代碼加密的話,可以對(duì)編譯后的代碼進(jìn)行加密,然后再通過實(shí)現(xiàn)自己的自定義類加載器進(jìn)行解密,最后再加載。
另一方面也有可能從非標(biāo)準(zhǔn)的來源加載代碼,比如從網(wǎng)絡(luò)來源,那就需要自己實(shí)現(xiàn)一個(gè)類加載器,從指定源進(jìn)行加載。

  • 驗(yàn)證

主要是為了保證加載進(jìn)來的字節(jié)流符合虛擬機(jī)規(guī)范,不會(huì)造成安全錯(cuò)誤。

包括對(duì)于文件格式的驗(yàn)證,比如常量中是否有不被支持的常量?文件中是否有不規(guī)范的或者附加的其他信息?

對(duì)于元數(shù)據(jù)的驗(yàn)證,比如該類是否繼承了被final修飾的類?類中的字段,方法是否與父類沖突?是否出現(xiàn)了不合理的重載?

對(duì)于字節(jié)碼的驗(yàn)證,保證程序語義的合理性,比如要保證類型轉(zhuǎn)換的合理性。

對(duì)于符號(hào)引用的驗(yàn)證,比如校驗(yàn)符號(hào)引用中通過全限定名是否能夠找到對(duì)應(yīng)的類?校驗(yàn)符號(hào)引用中的訪問性(private,public等)是否可被當(dāng)前類訪問?

  • 準(zhǔn)備

主要是為類變量(注意,不是實(shí)例變量)分配內(nèi)存,并且賦予初值。

特別需要注意,初值,不是代碼中具體寫的初始化的值,而是Java虛擬機(jī)根據(jù)不同變量類型的默認(rèn)初始值。

比如8種基本類型的初值,默認(rèn)為0;引用類型的初值則為null;常量的初值即為代碼中設(shè)置的值,final
static tmp = 456, 那么該階段tmp的初值就是456。

  • 解析

將常量池內(nèi)的符號(hào)引用替換為直接引用的過程。

兩個(gè)重點(diǎn):

符號(hào)引用:即一個(gè)字符串,但是這個(gè)字符串給出了一些能夠唯一性識(shí)別一個(gè)方法,一個(gè)變量,一個(gè)類的相關(guān)信息。

直接引用:可以理解為一個(gè)內(nèi)存地址,或者一個(gè)偏移量。比如類方法,類變量的直接引用是指向方法區(qū)的指針;而實(shí)例方法,實(shí)例變量的直接引用則是從實(shí)例的頭指針開始算起到這個(gè)實(shí)例變量位置的偏移量。

舉個(gè)例子來說,現(xiàn)在調(diào)用方法hello(),這個(gè)方法的地址是1234567,那么hello就是符號(hào)引用,1234567就是直接引用。

在解析階段,虛擬機(jī)會(huì)把所有的類名,方法名,字段名這些符號(hào)引用替換為具體的內(nèi)存地址或偏移量,也就是直接引用。

  • 初始化

這個(gè)階段主要是對(duì)類變量初始化,是執(zhí)行類構(gòu)造器的過程。
換句話說,只對(duì)static修飾的變量或語句進(jìn)行初始化。
如果初始化一個(gè)類的時(shí)候,其父類尚未初始化,則優(yōu)先初始化其父類。
如果同時(shí)包含多個(gè)靜態(tài)變量和靜態(tài)代碼塊,則按照自上而下的順序依次執(zhí)行。

  • 總結(jié)

類加載過程只是一個(gè)類生命周期的一部分,在其前,有編譯的過程,只有對(duì)源代碼編譯之后,才能獲得能夠被虛擬機(jī)加載的字節(jié)碼文件;在其后還有具體的類使用過程,當(dāng)使用完成之后,還會(huì)在方法區(qū)垃圾回收的過程中進(jìn)行卸載。如果想要了解Java類整個(gè)生命周期的話,可以自行上網(wǎng)查閱相關(guān)資料,這里不再多做贅述。

第三題:JVM內(nèi)存相關(guān)(云從科技)

問:Java 中會(huì)存在內(nèi)存泄漏嗎,請(qǐng)簡(jiǎn)單描述

答:

理論上Java因?yàn)橛欣厥諜C(jī)制(GC)不會(huì)存在內(nèi)存泄露問題(這也是Java被廣泛使用于服務(wù)器端編程的一個(gè)重要原因);然而在實(shí)際開發(fā)中,可能會(huì)存在無用但可達(dá)的對(duì)象,這些對(duì)象不能被GC回收也會(huì)發(fā)生內(nèi)存泄露

一個(gè)例子就是Hibernate的Session(一級(jí)緩存)中的對(duì)象屬于持久態(tài),垃圾回收器是不會(huì)回收這些對(duì)象的,然而這些對(duì)象中可能存在無用的垃圾對(duì)象。

下面的例子也展示了Java中發(fā)生內(nèi)存泄露的情況:

package com.yuan_more;import java.util.Arrays;import java.util.EmptyStackException;public class MyStack<T> {
    private  T[] elements;
    private int size = 0;

    private static final int INIT_CAPACITY = 16;

    public MyStack(){
        elements = (T[]) new Object[INIT_CAPACITY];
    }

    public void push(T elem){
        ensureCapacity();
    }

    public T pop(){
        if(size == 0){
            throw new EmptyStackException();
        }
        return elements[-- size];
    }

    private void ensureCapacity() {
        if(elements.length == size){
            elements = Arrays.copyOf(elements,2 * size +1);
        }
    }
}

上面的代碼實(shí)現(xiàn)了一個(gè)棧(先進(jìn)后出(FILO))結(jié)構(gòu),乍看之下似乎沒有什么明顯的問題,它甚至可以通過你編寫的各種單元測(cè)試。

然而其中的pop方法卻存在內(nèi)存泄露的問題,當(dāng)我們用pop方法彈出棧中的對(duì)象時(shí),該對(duì)象不會(huì)被當(dāng)作垃圾回收,即使使用棧的程序不再引用這些對(duì)象,因?yàn)闂?nèi)部維護(hù)著對(duì)這些對(duì)象的過期引用(obsolete reference)。

在支持垃圾回收的語言中,內(nèi)存泄露是很隱蔽的,這種內(nèi)存泄露其實(shí)就是無意識(shí)的對(duì)象保持。

如果一個(gè)對(duì)象引用被無意識(shí)的保留起來了,那么垃圾回收器不會(huì)處理這個(gè)對(duì)象,也不會(huì)處理該對(duì)象引用的其他對(duì)象,即使這樣的對(duì)象只有少數(shù)幾個(gè),也可能會(huì)導(dǎo)致很多的對(duì)象被排除在垃圾回收之外,從而對(duì)性能造成重大影響,極端情況下會(huì)引發(fā)Disk Paging(物理內(nèi)存與硬盤的虛擬內(nèi)存交換數(shù)據(jù)),甚至造成OutOfMemoryError。

第四題:垃圾回收相關(guān)(滴滴出行)

問:知道 GC 嗎?為什么要有 GC?

答:

GC是垃圾收集的意思,內(nèi)存處理是編程人員容易出現(xiàn)問題的地方,忘記或者錯(cuò)誤的內(nèi)存回收會(huì)導(dǎo)致程序或系統(tǒng)的不穩(wěn)定甚至崩潰。

Java提供的 GC 功能可以自動(dòng)監(jiān)測(cè)對(duì)象是否超過作用域從而達(dá)到自動(dòng)回收內(nèi)存的目的,Java語言沒有提供釋放已分配內(nèi)存的顯示操作方法。Java程序員不用擔(dān)心內(nèi)存管理,因?yàn)槔占鲿?huì)自動(dòng)進(jìn)行管理

要請(qǐng)求垃圾收集,可以調(diào)用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,注意,只是請(qǐng)求,JVM何時(shí)進(jìn)行垃圾回收具有不可預(yù)知性。

垃圾回收可以有效的防止內(nèi)存泄露,有效的使用可以使用的內(nèi)存。垃圾回收器通常是作為一個(gè)單獨(dú)的低優(yōu)先級(jí)的線程運(yùn)行,不可預(yù)知的情況下對(duì)內(nèi)存堆中已經(jīng)死亡的或者長(zhǎng)時(shí)間沒有使用的對(duì)象進(jìn)行清除和回收,程序員不能實(shí)時(shí)的調(diào)用垃圾回收器對(duì)某個(gè)對(duì)象或所有對(duì)象進(jìn)行垃圾回收。

在Java誕生初期,垃圾回收是Java最大的亮點(diǎn)之一,因?yàn)榉?wù)器端的編程需要有效的防止內(nèi)存泄露問題,然而時(shí)過境遷,如今Java的垃圾回收機(jī)制已經(jīng)成為被詬病的東西。移動(dòng)智能終端用戶通常覺得iOS的系統(tǒng)比Android系統(tǒng)有更好的用戶體驗(yàn),其中一個(gè)深層次的原因就在于Android系統(tǒng)中垃圾回收的不可預(yù)知性。

第五題:JVM內(nèi)存相關(guān)(阿里)

問:Hotspot虛擬機(jī)中的堆為什么要有新生代和老年代?

答:

因?yàn)橛械膶?duì)象壽命長(zhǎng),有的對(duì)象壽命短。應(yīng)該將壽命長(zhǎng)的對(duì)象放在一個(gè)區(qū),壽命短的對(duì)象放在一個(gè)區(qū)。不同的區(qū)采用不同的垃圾收集算法。壽命短的區(qū)清理頻次高一點(diǎn),壽命長(zhǎng)的區(qū)清理頻次低一點(diǎn),提高效率。

所謂的新生代和老年代是針對(duì)于分代收集算法來定義的,新生代又分為Eden和Survivor兩個(gè)區(qū)。加上老年代就這三個(gè)區(qū)。

數(shù)據(jù)會(huì)首先分配到Eden區(qū)當(dāng)中,當(dāng)然也有特殊情況,如果是大對(duì)象那么會(huì)直接放入到老年代(大對(duì)象是指需要大量連續(xù)內(nèi)存空間的java對(duì)象)。當(dāng)Eden沒有足夠空間的時(shí)候就會(huì)觸發(fā)jvm發(fā)起一次Minor GC。新生代垃圾回收采用的是復(fù)制算法

如果對(duì)象經(jīng)過一次Minor GC還存活,并且又能被Survivor空間接受,那么將被移動(dòng)到Survivor空間當(dāng)中。并將其年齡設(shè)為1,對(duì)象在Survivor每熬過一次Minor GC,年齡就加1,當(dāng)年齡達(dá)到一定的程度(默認(rèn)為15)時(shí),就會(huì)被晉升到老年代中了,當(dāng)然晉升老年代的年齡是可以設(shè)置的。如果老年代滿了就執(zhí)行:Full GC, 因?yàn)椴唤?jīng)常執(zhí)行,因此老年代垃圾回收采用了標(biāo)記-整理(Mark-Compact)算法

到此,相信大家對(duì)“JVM面試真題有哪些”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

jvm
AI