目錄
前言
一、解釋Java堆空間及GC?
二、JVM 的主要組成部分及其作用?
三、JVM 運行時資料區
四、深拷貝和淺拷貝
五、堆疊的區別
六、佇列和堆疊是什么?有什么區別?
七、物件的創建
八、為物件分配記憶體
1.分配的方式
2.處理并發安全問題
九、34位和64位系統間的問題
1.64 位 JVM 中,int 的長度是多數?
2.怎樣通過 Java 程式來判斷 JVM 是 32 位 還是 64位?
3.32 位 JVM 和 64 位 JVM 的最大堆記憶體分別是多數?
4.JRE、JDK、JVM 及 JIT 之間有什么不同?
總結
前言
剛開始慢慢深入去了解Java記憶體區域的內容,平時也都是用閑余時間來學習的,記憶體區域的基礎知識挺多,在這里整理出來可以提供給大家借鑒下,也方便自己復習,
(如有侵權,請通知告知,核對必刪!)
一、解釋Java堆空間及GC?
當通過 Java 命令啟動 Java 行程的時候,會為它分配記憶體,記憶體的一部分用于創建堆空間,當程式中創建物件的時候,就從堆空間中分配記憶體,
GC 是 JVM 內部的一個行程,回收無效物件的記憶體用于將來的分配,
二、JVM 的主要組成部分及其作用?
JVM包含兩個子系統和兩個組件,兩個子系統為Class loader(類裝載)、Execution engine(執行引擎);兩個組件為Runtime data area(運行時資料區)、Native Interface(本地介面),
- Class loader(類裝載):根據給定的全限定名類名(如:java.lang.Object)來裝載class檔案到Runtime data area中的method area,
- Execution engine(執行引擎):執行classes中的指令,
- Native Interface(本地介面):與native libraries互動,是其它編程語言互動的介面,
- Runtime data area(運行時資料區域):這就是我們常說的JVM的記憶體,
作用 :首先通過編譯器把 Java 代碼轉換成位元組碼,類加載器(ClassLoader)再把位元組碼加載到記憶體中,將其放在運行時資料區(Runtime data area)的方法區內,而位元組碼檔案只是 JVM 的一套指令集規范,并不能直接交給底層作業系統去執行,因此需要特定的命令決議器執行引擎(Execution Engine),將位元組碼翻譯成底層系統指令,再交由 CPU 去執行,而這個程序中需要呼叫其他語言的本地庫介面(Native Interface)來實作整個程式的功能,
下面是Java程式運行機制詳細說明
Java程式運行機制步驟
- 首先利用IDE集成開發工具撰寫Java源代碼,源檔案的后綴為.java;
- 再利用編譯器(javac命令)將源代碼編譯成位元組碼檔案,位元組碼檔案的后綴名為.class;
- 運行位元組碼的作業是由解釋器(java命令)來完成的,
從上圖可以看,java檔案通過編譯器變成了.class檔案,接下來類加載器又將這些.class檔案加載到JVM中,
其實可以一句話來解釋:類的加載指的是將類的.class檔案中的二進制資料讀入到記憶體中,將其放在運行時資料區的方法區內,然后在堆區創建一個 java.lang.Class物件,用來封裝類在方法區內的資料結構,
三、JVM 運行時資料區
- 程式計數器(Program Counter Register):當前執行緒所執行的位元組碼的行號指示器,位元組碼決議器的作業是通過改變這個計數器的值,來選取下一條需要執行的位元組碼指令,分支、回圈、跳轉、例外處理、執行緒恢復等基礎功能,都需要依賴這個計數器來完成;執行緒私有
- Java 虛擬機堆疊(Java Virtual Machine Stacks):用于存盤區域變數表、運算元堆疊、動態鏈接、方法出口等資訊;執行緒私有
- 本地方法堆疊(Native Method Stack):與虛擬機堆疊的作用是一樣的,只不過虛擬機堆疊是服務 Java 方法的,而本地方法堆疊是為虛擬機呼叫 Native 方法服務的;
- Java 堆(Java Heap):Java 虛擬機中記憶體最大的一塊,是被所有執行緒共享的,幾乎所有的物件實體都在這里分配記憶體;
- 方法區(Methed Area):用于存盤已被虛擬機加載的類資訊、常量、靜態變數、即時編譯后的代碼等資料,
四、深拷貝和淺拷貝
淺拷貝(shallowCopy)只是增加了一個指標指向已存在的記憶體地址,
深拷貝(deepCopy)是增加了一個指標并且申請了一個新的記憶體,使這個增加的指標指向這個新的記憶體,
使用深拷貝的情況下,釋放記憶體的時候不會因為出現淺拷貝時釋放同一個記憶體的錯誤,
淺復制:僅僅是指向被復制的記憶體地址,如果原地址發生改變,那么淺復制出來的物件也會相應的改變,
深復制:在計算機中開辟一塊新的記憶體地址用于存放復制的物件,
五、堆疊的區別
堆和堆疊的區別我們可以從四個煩面來分析比較,物理地址、記憶體區別、儲存物件、可見性
物理地址:
堆的物理地址分配給物件是不連續的,因此性能慢些,在GC的時候也要考慮到不連續的分配,所以有各種演算法,比如,標記-消除,復制,標記-壓縮,分代(即新生代使用復制演算法,老年代使用標記——壓縮)
堆疊使用的是資料結構中的堆疊,先進后出的原則,物理地址分配是連續的,所以性能快,
記憶體區別:
堆因為是不連續的,所以分配的記憶體是在運行期
確認的,因此大小不固定,一般堆大小遠遠大于堆疊,
堆疊是連續的,所以分配的記憶體大小要在編譯期
就確認,大小是固定的,
儲存物件:
堆存放的是物件的實體和陣列,因此堆更關注的是資料的存盤
堆疊存放:區域變數,運算元堆疊,回傳結果,堆疊更關注的是程式方法的執行,
PS:
- 靜態變數放在方法區
- 靜態的物件還是放在堆,
可見性:
堆對于整個應用程式都是共享、可見的,
堆疊只對于執行緒是可見的,所以也是執行緒私有,他的生命周期和執行緒相同,
六、佇列和堆疊是什么?有什么區別?
佇列和堆疊都是被用來預存盤資料的,
- 操作的名稱不同,佇列的插入稱為入隊,佇列的洗掉稱為出隊,堆疊的插入稱為進堆疊,堆疊的洗掉稱為出堆疊,
- 可操作的方式不同,佇列是在隊尾入隊,隊頭出隊,即兩邊都可操作,而堆疊的進堆疊和出堆疊都是在堆疊頂進行的,無法對堆疊底直接進行操作,
- 操作的方法不同,佇列是先進先出(FIFO),即佇列的修改是依先進先出的原則進行的,新來的成員總是加入隊尾(不能從中間插入),每次離開的成員總是佇列頭上(不允許中途離隊),而堆疊為后進先出(LIFO),即每次洗掉(出堆疊)的總是當前堆疊中最新的元素,即最后插入(進堆疊)的元素,而最先插入的被放在堆疊的底部,要到最后才能洗掉,
七、物件的創建
Java中物件創建的集中方式:
Header | 解釋 |
---|---|
使用new關鍵字 | 呼叫了建構式 |
使用Class的newInstance方法 | 呼叫了建構式 |
使用Constructor類的newInstance方法 | 呼叫了建構式 |
使用clone方法 | 沒有呼叫建構式 |
使用反序列化 | 沒有呼叫建構式 |
物件創建的主要流程:
八、為物件分配記憶體
1.分配的方式
類加載完成后,接著會在Java堆中劃分一塊記憶體分配給物件,記憶體分配根據Java堆是否規整,有兩種方式:
- 指標碰撞:如果Java堆的記憶體是規整,即所有用過的記憶體放在一邊,而空閑的的放在另一邊,分配記憶體時將位于中間的指標指示器向空閑的記憶體移動一段與物件大小相等的距離,這樣便完成分配記憶體作業,
- 空閑串列:如果Java堆的記憶體不是規整的,則需要由虛擬機維護一個串列來記錄那些記憶體是可用的,這樣在分配的時候可以從串列中查詢到足夠大的記憶體分配給物件,并在分配后更新串列記錄,
選擇哪種分配方式是由 Java 堆是否規整來決定的,而 Java 堆是否規整又由所采用的垃圾收集器是否帶有壓縮整理功能決定,
2.處理并發安全問題
物件的創建在虛擬機中是一個非常頻繁的行為,哪怕只是修改一個指標所指向的位置,在并發情況下也是不安全的,可能出現正在給物件 A 分配記憶體,指標還沒來得及修改,物件 B 又同時使用了原來的指標來分配記憶體的情況,解決這個問題有兩種方案:
- 對分配記憶體空間的動作進行同步處理(采用 CAS + 失敗重試來保障更新操作的原子性);
- 把記憶體分配的動作按照執行緒劃分在不同的空間之中進行,即每個執行緒在 Java 堆中預先分配一小塊記憶體,稱為本地執行緒分配緩沖(Thread Local Allocation Buffer, TLAB),哪個執行緒要分配記憶體,就在哪個執行緒的 TLAB 上分配,只有 TLAB 用完并分配新的 TLAB 時,才需要同步鎖,通過-XX:+/-UserTLAB引數來設定虛擬機是否使用TLAB,
九、34位和64位系統間的問題
1.64 位 JVM 中,int 的長度是多數?
32 位和 64 位的 JVM 中,int 型別變數的長度是相同的,都是 32 位或者 4個位元組,
2.怎樣通過 Java 程式來判斷 JVM 是 32 位 還是 64位?
你可以檢查某些系統屬性如 sun.arch.data.model 或 os.arch 來獲取該資訊,
3.32 位 JVM 和 64 位 JVM 的最大堆記憶體分別是多數?
理論上說上 32 位的 JVM 堆記憶體可以到達 2^32, 即 4GB,但實際上會比這個小很多,不同作業系統之間不同,如 Windows 系統大約 1.5GB,Solaris 大約3GB,64 位 JVM 允許指定最大的堆記憶體,理論上可以達到 2^64,這是一個非常大的數字,實際上你可以指定堆記憶體大小到 100GB,甚至有的 JVM,如 Azul,堆記憶體到 1000G 都是可能的,
4.JRE、JDK、JVM 及 JIT 之間有什么不同?
JRE 代表 Java 運行時(Java run-time),是運行 Java 參考所必須的,
JDK 代表 Java 開發工具(Java development kit),是 Java 程式的開發工具,如 Java編譯器,它也包含 JRE,
JVM 代表 Java 虛擬機(Java virtual machine),它的責任是運行 Java 應用,
JIT 代表即時編譯(Just In Time compilation),當代碼執行的次數超過一定的閾值時,會將 Java 位元組碼轉換為本地代碼,如,主要的熱點代碼會被準換為本地代碼,這樣有利大幅度提高 Java 應用的性能,
總結
Java記憶體區域的知識點還是蠻多的,后續有時間就在博客上發出來,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/295518.html
標籤:其他