天道不一定酬所有勤
但是,天道只酬勤

英雄联盟角色:萬萬沒想到,JVM內存結構的面試題可以問的這么難?

英雄联盟充值记录 www.frhrb.icu 在我的博客中,之前有很多文章介紹過JVM內存結構,相信很多看多我文章的朋友對這部分知識都有一定的了解了。

那么,請大家嘗試著回答一下以下問題:

1、JVM管理的內存結構是怎樣的?
2、不同的虛擬機在實現運行時內存的時候有什么區別?
3、運行時數據區中哪些區域是線程共享的?哪些是獨享的?
4、除了JVM運行時內存以外,還有什么區域可以用嗎?
5、堆和棧的區別是什么?
6、Java中的數組是存儲在堆上還是棧上的?
7、Java中的對象創建有多少種方式?
8、Java中對象創建的過程是怎么樣的?
9、Java中的對象一定在堆上分配內存嗎?
10、如何獲取堆和棧的dump文件?

以上10道題,如果您可以全部準確無誤的回答的話,那說明你真的很了解JVM的內存結構以及內存分配相關的知識了,如果有哪些知識點是不了解的,那么本文正好可以幫你答疑解惑。

JVM管理的內存結構是怎樣的?

Java虛擬機在執行Java程序的過程中會把他所管理的內存劃分為若干個不同的數據區域?!禞ava虛擬機規范》中規定了JVM所管理的內存需要包括一下幾個運行時區域:

?

主要包含了PC寄存器(程序計數器)、Java虛擬機棧、本地方法棧、Java堆、方法區以及運行時常量池。

各個區域有各自不同的作用,關于各個區域的作用就不在本文中相信介紹了。

但是,需要注意的是,上面的區域劃分只是邏輯區域,對于有些區域的限制是比較松的,所以不同的虛擬機廠商在實現上,甚至是同一款虛擬機的不同版本也是不盡相同的。

不同的虛擬機在實現運行時內存的時候有什么區別?

前面提到過《Java虛擬機規范》定義的JVM運行時所需的內存區域,不同的虛擬機實現上有所不同,而在這么多區域中,規范對于方法區的管理是最寬松的,規范中關于這部分的描述如下:

方法區在虛擬機啟動的時候創建,雖然方法區是堆的邏輯組成部分,但是簡單的虛擬機實現可以選擇在這個區域不實現垃圾收集與壓縮。本版本的規范也不限定實現方法區的內存位置和代碼編譯的管理策略。方法區的容量可以是固定的,也可以隨著程序執行的需求動態擴展,并在不需要過多的空間時自行收縮。方法區在實際內存空間站可以是不連續的。

這一規定,可以說是給了虛擬機廠商很大的自由。

虛擬機規范對方法區實現的位置并沒有明確要求,在最著名的HotSopt虛擬機實現中(在Java 8 之前),方法區僅是邏輯上的獨立區域,在物理上并沒有獨立于堆而存在,而是位于永久代中。所以,這時候方法區也是可以被垃圾回收的。

實踐證明,JVM中存在著大量的聲明短暫的對象,還有一些生命周期比較長的對象。為了對他們采用不同的收集策略,采用了分代收集算法,所以HotSpot虛擬機把的根據對象的年齡不同,把堆分位新生代、老年代和永久代。

在Java 8中 ,HotSpot虛擬機移除了永久代,使用本地內存來存儲類元數據信息并稱之為:元空間(Metaspace)

?

運行時數據區中哪些區域是線程共享的?哪些是獨享的?

在JVM運行時內存區域中,PC寄存器、虛擬機棧和本地方法棧是線程獨享的。

而Java堆、方法區是線程共享的。但是值得注意的是,Java堆其實還未每一個線程單獨分配了一塊TLAB空間,這部分空間在分配時是線程獨享的,在使用時是線程共享的。

除了JVM運行時內存以外,還有什么區域可以用嗎?

除了我們前面介紹的虛擬機運行時數據區以外,還有一部分內存也被頻繁使用,他不是運行時數據區的一部分,也不是Java虛擬機規范中定義的內存區域,他就是——直接內存。

直接內存的分配不受Java堆大小的限制,但是他還是會收到服務器總內存的影響。

在JDK 1.4中引入的NIO中,引入了一種基于Channel和Buffer的I/O方式,他可以使用Native函數直接分配堆外內存,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內存的應用進行操作。

?

堆和棧的區別是什么?

堆和棧(虛擬機棧)是完全不同的兩塊內存區域,一個是線程獨享的,一個是線程共享的,二者之間最大的區別就是存儲的內容不同:

堆中主要存放對象實例。
棧(局部變量表)中主要存放各種基本數據類型、對象的引用。

Java中的數組是存儲在堆上還是棧上的?

在Java中,數組同樣是一個對象,所以對象在內存中如何存放同樣適用于數組;

所以,數組的實例是保存在堆中,而數組的引用是保存在棧上的。

?

Java中的對象創建有多少種方式?

Java中有很多方式可以創建一個對象,最簡單的方式就是使用new關鍵字。

User user = new User();

除此以外,還可以使用反射機制創建對象:

User user = User.class.newInstance();

或者使用Constructor類的newInstance:

Constructor<User> constructor = User.class.getConstructor();
User user = constructor.newInstance();

除此之外還可以使用clone方法和反序列化的方式,這兩種方式不常用并且代碼比較復雜,就不在這里展示了,感興趣的可以自行了解下。

Java中對象創建的過程是怎么樣的?

對于一個普通的Java對象的創建,大致過程如下:

1、虛擬機遇到new指令,到常量池定位到這個類的符號引用。
2、檢查符號引用代表的類是否被加載、解析、初始化過。
3、虛擬機為對象分配內存。
4、虛擬機將分配到的內存空間都初始化為零值。
5、虛擬機對對象進行必要的設置。
6、執行方法,成員變量進行初始化。

Java中的對象一定在堆上分配內存嗎?

前面我們說過,Java堆中主要保存了對象實例,但是,隨著JIT編譯期的發展與逃逸分析技術逐漸成熟,棧上分配、標量替換優化技術將會導致一些微妙的變化,所有的對象都分配到堆上也漸漸變得不那么“絕對”了。

其實,在編譯期間,JIT會對代碼做很多優化。其中有一部分優化的目的就是減少內存堆分配壓力,其中一種重要的技術叫做逃逸分析。

如果JIT經過逃逸分析,發現有些對象沒有逃逸出方法,那么有可能堆內存分配會被優化成棧內存分配。

?

10、如何獲取堆和棧的dump文件?

Java Dump,Java虛擬機的運行時快照。將Java虛擬機運行時的狀態和信息保存到文件。

可以使用在服務器上使用jmap命令來獲取堆dump,使用jstack命令來獲取線程的調用棧dump。

(全文完) 歡迎關注『Java之道』微信公眾號
贊(8)
如未加特殊說明,此網站文章均為原創,轉載必須注明出處。英雄联盟充值记录 » 萬萬沒想到,JVM內存結構的面試題可以問的這么難?
分享到: 更多 (0)

評論 搶沙發

  • 昵稱 (必填)
  • 郵箱 (必填)
  • 網址

HollisChuang's Blog

聯系我關于我