在個人博客中,以前有不少文章介紹過JVM內存結構,相信不少看多我文章的朋友對這部分知識都有必定的瞭解了。算法
那麼,請你們嘗試着回答一下如下問題:數組
一、JVM管理的內存結構是怎樣的?
二、不一樣的虛擬機在實現運行時內存的時候有什麼區別?
三、運行時數據區中哪些區域是線程共享的?哪些是獨享的?
四、除了JVM運行時內存之外,還有什麼區域能夠用嗎?
五、堆和棧的區別是什麼?
六、Java中的數組是存儲在堆上仍是棧上的?
七、Java中的對象建立有多少種方式?
八、Java中對象建立的過程是怎麼樣的?
九、Java中的對象必定在堆上分配內存嗎?
十、如何獲取堆和棧的dump文件?服務器
以上10道題,若是您能夠所有準確無誤的回答的話,那說明你真的很瞭解JVM的內存結構以及內存分配相關的知識了,若是有哪些知識點是不瞭解的,那麼本文正好能夠幫你答疑解惑。函數
JVM管理的內存結構是怎樣的?
Java虛擬機在執行Java程序的過程當中會把他所管理的內存劃分爲若干個不一樣的數據區域。《Java虛擬機規範》中規定了JVM所管理的內存須要包括一下幾個運行時區域:優化
spa
主要包含了PC寄存器(程序計數器)、Java虛擬機棧、本地方法棧、Java堆、方法區以及運行時常量池。線程
各個區域有各自不一樣的做用,關於各個區域的做用就不在本文中相信介紹了。code
可是,須要注意的是,上面的區域劃分只是邏輯區域,對於有些區域的限制是比較鬆的,因此不一樣的虛擬機廠商在實現上,甚至是同一款虛擬機的不一樣版本也是不盡相同的。對象
不一樣的虛擬機在實現運行時內存的時候有什麼區別?
前面提到過《Java虛擬機規範》定義的JVM運行時所需的內存區域,不一樣的虛擬機實現上有所不一樣,而在這麼多區域中,規範對於方法區的管理是最寬鬆的,規範中關於這部分的描述以下:blog
方法區在虛擬機啓動的時候建立,雖然方法區是堆的邏輯組成部分,可是簡單的虛擬機實現能夠選擇在這個區域不實現垃圾收集與壓縮。本版本的規範也不限定實現方法區的內存位置和代碼編譯的管理策略。方法區的容量能夠是固定的,也能夠隨着程序執行的需求動態擴展,並在不須要過多的空間時自行收縮。方法區在實際內存空間站能夠是不連續的。
這一規定,能夠說是給了虛擬機廠商很大的自由。
虛擬機規範對方法區實現的位置並無明確要求,在最著名的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對象的建立,大體過程以下:
一、虛擬機遇到new指令,到常量池定位到這個類的符號引用。
二、檢查符號引用表明的類是否被加載、解析、初始化過。
三、虛擬機爲對象分配內存。
四、虛擬機將分配到的內存空間都初始化爲零值。
五、虛擬機對對象進行必要的設置。
六、執行方法,成員變量進行初始化。
Java中的對象必定在堆上分配內存嗎?
前面咱們說過,Java堆中主要保存了對象實例,可是,隨着JIT編譯期的發展與逃逸分析技術逐漸成熟,棧上分配、標量替換優化技術將會致使一些微妙的變化,全部的對象都分配到堆上也漸漸變得不那麼「絕對」了。
其實,在編譯期間,JIT會對代碼作不少優化。其中有一部分優化的目的就是減小內存堆分配壓力,其中一種重要的技術叫作逃逸分析。
若是JIT通過逃逸分析,發現有些對象沒有逃逸出方法,那麼有可能堆內存分配會被優化成棧內存分配。
十、如何獲取堆和棧的dump文件?
Java Dump,Java虛擬機的運行時快照。將Java虛擬機運行時的狀態和信息保存到文件。
可使用在服務器上使用jmap命令來獲取堆dump,使用jstack命令來獲取線程的調用棧dump。