更新時間:2023-02-03 11:49:09 來源:動力節(jié)點 瀏覽1270次
Jvm既可以操作系統(tǒng)平臺相關(guān)信息又可以做到屏蔽作用,這就可以讓Java程序只需要在Java虛擬機上實現(xiàn)在不同的平臺上運行,所以我們在面試相關(guān)崗位的時候,Java Jvm方面的面試題會被頻繁的問到,所以,正準備面試的同學,Java Jvm的面試題最好了解一些。
1.運行時數(shù)據(jù)區(qū)是什么
虛擬機在執(zhí)行程序過程中,將虛擬機內(nèi)部的內(nèi)存區(qū)域劃分成不同的數(shù)據(jù)區(qū)域。每個數(shù)據(jù)區(qū)都具有自己的生命周期和功能作用。
對于這一部分區(qū)域,我們可以根據(jù)線程的私有和線程共享來進行分類。
從線程獨占角度 來看主要又 ;pc程序計數(shù)器 ,java虛擬機棧 ,本地方法棧
而線程共享來看則主要是的:堆和方法區(qū)。
2.堆的作用是什么
答題整體思路:點名堆內(nèi)存的主要作用,之后深入至堆內(nèi)存中年輕代、老年代的整體的布局情況。可以順帶提及minorgc和Full gc發(fā)生的時機。
如果時間充裕的話還可以擴展衍生至對象創(chuàng)建,對象分配的話題。一個小小的堆內(nèi)存可以回答和擴展的點真的很多。
關(guān)于JVM中的堆內(nèi)存,其主要功能就是用于存放java中的對象實例信息,同時在java虛擬機規(guī)范中也明確指出,對象信息和數(shù)組信息的創(chuàng)建應該帶都分配在堆內(nèi)存之中。
當我們知道了堆的主要作用后,接下來我們可以看看jvm的基本內(nèi)存布局,整體來看在jvm的堆內(nèi)存中,其主要主要存儲區(qū)域分為年輕代和老年代。如果使用的版本是jdk8之前的版本則還有永久代的概念,而到了jdk8之后永久代則被元空間所取代。
對于我們的堆內(nèi)存的年輕代來看,其內(nèi)部又劃分為兩個survior區(qū)和一個eden區(qū)。這樣設計的目的也主要是為了能更好的進行垃圾回收。我們都知道java中的對象通常朝生夕死,大部分對象的存活生命周期還是很短的,為了降低垃圾回收的頻率,提升jvm的整體性能。所以設定兩個survior區(qū)來進行垃圾的回收和處理。
如果細致來看,此時我們對于survior為什么設定兩個?討論這個問題的前提在于我們假定垃圾回收采用的算法是復制算法。
對于復制算法其主要過程如下:
1)Eden 區(qū)活著的對象 + From Survivor 存儲的對象被復制到 To Survivor ;
2) 清空 Eden 和 From Survivor ;
3) 顛倒 From Survivor 和 To Survivor 的邏輯關(guān)系: From 變 To , To 變 From 。
說清楚復制算法之后,我們在開始討論為什么設定surivor內(nèi)存區(qū)域為兩個。
這個問題我們來看0個,如果不設定survior區(qū)域,那么eden區(qū)滿了之后就需要觸發(fā)gc,此時幸存對象保存至老年代,這樣老年代很快就會被填滿,加劇了full gc所帶來的性能損耗。
如果設定一個,我們都知道對于年輕代區(qū)而言,Eden區(qū)和suriovr區(qū)域的大小比為8:1,我們假設內(nèi)存為9M則,eden占據(jù)8M,surivior占據(jù)1M,此時如果幸存區(qū)內(nèi)容為0.5M則此時我們,此時surivior很快被填滿,同時如果surivior作為對象分配內(nèi)存的起點,此時又是會很快被填滿。
在只有一個 Survivor 的情況下,無論 Eden 和 Survivor 的比例怎么設置,總體上看在新生代空間滿一半的時候就會觸發(fā)一次 Minor gc 。那有沒有提升的空間呢?比如說永遠在新生代空間滿 80% 的時候才觸發(fā) Minor gc ?
此時就引出設計兩個serviceor空間( From Survivor 和 To Survivor )的設計思路。此時把 Eden : From Survivor : To Survivor 空間大小設成 8 : 1 : 1 。
對象總是在 Eden 區(qū)出生, From Survivor 保存當前的幸存對象, To Survivor 為空。這樣僅當eden區(qū)滿之后才會觸發(fā)minorgc,垃圾回收的頻率被進一步降低,性能的影響也得以平衡。
3.方法區(qū)的作用是什么?
方法區(qū)用于存儲被虛擬機加載的類型信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼緩存等數(shù)據(jù)。
JDK8 之前使用永久代實現(xiàn)方法區(qū),容易內(nèi)存溢出,因為永久代有 上限,即使不設置也有默認大小。JDK7 把放在永久代的字符串常量池、靜態(tài)變量等移出,JDK8 中永久代完全廢棄,改用在本地內(nèi)存中實現(xiàn)的元空間代替,把 JDK 7 中永久代剩余內(nèi)容(主要是類型信息)全部移到元空間。
虛擬機規(guī)范對方法區(qū)的約束寬松,除和堆一樣不需要連續(xù)內(nèi)存和可選擇固定大小/可擴展外,還可以不實現(xiàn)垃圾回收。垃圾回收在方法區(qū)出現(xiàn)較少,主要目標針對常量池和類型卸載。如果方法區(qū)無法滿足新的內(nèi)存分配需求,將拋出 OutOfMemoryError
4.運行時常量池作用以及其同類文件中常量池的區(qū)別
運行時常量池是方法區(qū)的一部分,Class 文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池表,用于存放編譯器生成的各種字面量與符號引用,這部分內(nèi)容在類加載后存放到運行時常量池。一般除了保存 Class 文件中描述的符號引用外,還會把符號引用翻譯的直接引用也存儲在運行時常量池。
運行時常量池相對于 Class 文件常量池的一個重要特征是動態(tài)性,Java 不要求常量只有編譯期才能產(chǎn)生,運行期間也可以將新的常量放入池中,這種特性利用較多的是 String 的 方法。
運行時常量池是方法區(qū)的一部分,受到方法區(qū)內(nèi)存的限制,當常量池無法再申請到內(nèi)存時會拋出 OutOfMemoryError。
對于class 文件中的常量池,我們將 java 代碼通過 javac 編譯為 class 文件,接下來再運行 java 程序的時候就完全不需要 java 文件了,只需要 class 文件即可。而 class 文件是保存在本地磁盤上的文件,里面全是0101的字節(jié)碼,或者說是字符串,那各個字符串之間如何產(chǎn)生聯(lián)系呢?那就是通過常量池。
對于class常量池而言其在編譯的時候每個class都有的,在編譯階段,主要存放的是常量的符號引用。
而運行時常量池是在類加載完成之后,將每個class常量池中的符號引用值轉(zhuǎn)存到運行時常量池中。兩者產(chǎn)生時機并不相同,同時類常量池時靜態(tài)的,而運行時常量池時動態(tài)的。
5.jvm中垃圾收集器有那些
一:Serial 收集器
Serial收集器是最基本的、發(fā)展歷史最悠久的收集器。
特點:單線程、簡單高效,對于限定單個CPU的環(huán)境來說,Serial收集器由于沒有線程交互的開銷,專心做垃圾收集自然可以獲得最高的單線程手機效率。收集器進行垃圾回收時,必須暫停其他所有的工作線程,直到它結(jié)束(Stop The World)。
二:ParNew收集器
ParNew收集器其實就是Serial收集器的多線程版本。
除了使用多線程外其余行為均和Serial收集器一模一樣(參數(shù)控制、收集算法、Stop The World、對象分配規(guī)則、回收策略等)。
特點:多線程、ParNew收集器默認開啟的收集線程數(shù)與CPU的數(shù)量相同,在CPU非常多的環(huán)境中,可以使用-XX:ParallelgcThreads參數(shù)來限制垃圾收集的線程數(shù)。
三:Serial Old 收集器
Serial Old是Serial收集器的老年代版本。
特點:同樣是單線程收集器,但是面向老老年代的垃圾收集。采用標記-整理算法。
四:Parallel Old 收集器
概述:是Parallel Scavenge收集器的老年代版本。
特點:多線程,采用標記-整理算法。
五:CMS收集器
全稱(Concurrent Mark Sweep)一種以獲取最短回收停頓時間為目標的收集器。
特點:基于標記-清除算法實現(xiàn)。并發(fā)收集、低停頓。
應用場景:適用于注重服務的響應速度,希望系統(tǒng)停頓時間最短,給用戶帶來更好的體驗等場景下。如web程序、b/s服務。
因為采用標記-清除算法所以會存在空間碎片的問題,導致大對象無法分配空間,不得不提前觸發(fā)一次Full gc。
以上就是“大廠通用的Java jvm面試題”,你能回答上來嗎?如果想要了解更多的Java面試題相關(guān)內(nèi)容,可以關(guān)注動力節(jié)點Java官網(wǎng)。