什么時候以理解的目標學JVM?
這個問題是我從學習Java開始,即大二上冊開始,一直抱有的問題,我在網上搜索了很多次,都沒有告知我明確的答案,我想現在我可以勉強給個答案,我覺得:
-
作業系統學習過一遍
-
計算機組成原理學習過一遍
-
有一定的匯編語言基礎
-
Java SE有著扎實的基礎
-
有一定的并發編程基礎【因為虛擬機的設計都需要考慮高并發狀態】
為什么要學習Java記憶體區域和記憶體溢位例外?
Java程式員把控制記憶體的權力交給了Java虛擬機,一旦出現記憶體泄露和溢位方面的問題,如果不了解虛擬機的記憶體管理機制,修正錯誤將會成為艱難的一項作業,
運行時資料區域有哪幾類?什么時候會發生OOM?
程式計數器
定義:程式計數器是當前執行緒所執行的位元組碼的行號指示器(程式控制流指示器),程式的分支、回圈、跳轉、例外處理和執行緒恢復等繼承功能都依賴它來實作,特別的是,如果執行緒正在執行一個Java方法,那么它記錄著虛擬機位元組碼指令的地址;如果執行的是本地(關鍵字native)方法,那么它記錄著空值,
生命周期:它是執行緒私有的記憶體,每條執行緒都有一個獨立的程式計數器,各條執行緒之間互不影響,獨立存盤;隨用戶執行緒的啟動和結束而建立和銷毀,
OOM:沒有任何可能出現OOM的情況,
虛擬機堆疊
什么是堆疊幀?
每個方法被執行時,Java虛擬機都會同步創建一個堆疊幀,用于存盤區域變數表、運算元堆疊、動態鏈接、方法出口等方法有關的資訊【參考匯編語言堆疊的作用】,
什么是區域變數表?
區域變數表存盤了方法執行程序中所有的區域變數,包括:基本資料型別、物件參考、方法回傳地址(指向一條位元組碼指令的地址),
定義:每一個執行緒中,存盤堆疊幀的堆疊就是虛擬機堆疊,每一個方法從被呼叫到執行完畢的程序,就對應著一個堆疊幀在虛擬機堆疊中從入堆疊到出堆疊的程序,
生命周期:它是執行緒私有的記憶體,生命周期與執行緒相同,
OOM:堆疊深度超出了虛擬機的最大深度,拋出StackOverflow,而假如虛擬機堆疊支持動態擴展,則虛擬機堆疊無法申請到足夠的記憶體時,會拋出OutOfMemoryError(OOM),
本地方法堆疊
和虛擬機堆疊大致一樣,只不過本地方法堆疊存盤的是本地方法(關鍵字native)的堆疊幀,而虛擬機堆疊存盤的是Java方法的堆疊幀,
堆
定義:幾乎所有的物件實體以及陣列都在堆上分配,(new關鍵字)
為什么說是幾乎所有?
隨著即時編譯技術及逃逸分析技術的日漸強大,堆疊上分配、標量替換(應該是兩種編譯優化技術)已經導致物件實體有可能不在堆中而在堆疊中了!
什么是經典分代?
新生代和老年代,在GC回收演算法中它們會成為主角哦,
在我看來,為什么堆的生命周期沒有那么絕對?
大多物件實體是執行緒共享的(這個很明顯吧,這是執行緒安全問題存在的根本原因),但也有少部分是執行緒私有的,比如:ThreadLocal<T>它就是執行緒私有的堆記憶體區域和TLAB(下面會講),
從這,對自己說一句,可以明顯看出的是,大多事務都不絕對,一切都是為了更好的運行而設計的,而不是為了規則本身而設計的,別太死板,生命周期由GC回收演算法控制,
OOM:堆記憶體不足以進行物件實體記憶體分配并且堆再也無法動態拓展時,會拋出OOM,
方法區
這是最難理解的記憶體分配區域,極其勸退,我前六次都被它勸退了,概念很繞,hh,概括來說,就是這里存盤著有關類的一切資訊!
永久代、元空間和方法區是什么關系?
永久代、元空間都是方法區的一種實作,在永久代被廢除后,方法區的實作就采用元空間,
永久代和元空間有什么不同?
存盤位置不同,永久代是堆的一部分,和新生代,老年代地址是連續的,而元空間屬于本地記憶體;
存盤內容不同,元空間存盤類的元資訊,而靜態變數和常量池等并入堆中,相當于永久代的資料被分到了堆和元空間中,
但是,同樣的,對概念別太死板,雖然現在方法區的實作是元空間,但是,堆中的那部分資料仍然是屬于方法區的,也就是堆和方法區(又稱非堆)其實并沒有那么清晰的界限,都只不過是為了對記憶體進行更好的分配罷了,
定義:它存盤著虛擬機加載的型別資訊、常量、靜態變數和即時編譯器編譯后的代碼快取等長期存在的資料,
解釋什么是常量池?
-
字串常量池:存放字串常量和字串常量參考(intern存入的字串常量參考,new方法會創建一個字串物件同時存入一個字串常量到池中)的記憶體區域,里面字串常量不會重復,原本被歸類于方法區,因為長期不會被回收;后被歸類于堆,因為方法區太小,容不下這尊大佛了,
-
Class常量池:用于存放編譯器生成的Class檔案中的各種字面量(Literal)和符號參考(Symbolic References);
-
運行時常量池:運行時常量池可以在運行期間將符號參考決議為直接參考,由運行時常量池存盤這些直接參考,也就是說,運行時常量池存盤著Class常量池運行期間存入的符號參考,以及由符號參考決議出來的直接參考,還包括運行期間產生的新的常量(關于字串String中的intern方法,如果字串常量池中已經存在此字串常量,不變;而如果不存在,字串常量池存入此字串常量的參考)
OOM:當方法區(運行時常量池)無法滿足新的記憶體分配需求時,會拋出OOM,
什么是直接記憶體?
非JVM管理的一塊記憶體區域,
什么是NIO?
NIO是一種基于通道于緩沖區的IO方式,它可以使用Native函式庫直接分配堆外記憶體,然后通過堆中的DirectByteBuffer物件作為這塊記憶體的應用進行操作,顯著提高了Java應用程式的性能,(偷偷占了多的記憶體能不顯著嗎?)
產生OOM的原因?
作業系統剩余記憶體滿足不了直接記憶體的分配需求,
物件是如何創建的?

物件記憶體的分配方式有哪些?
指標碰撞:把指標向空閑空間方向挪動一段與物件大小相等的距離,
空閑串列:虛擬機維護一個串列,記錄哪塊記憶體是可用的,從串列中利用分配演算法找到一塊足夠大的空間劃分給物件實體,并更新串列,會產生外部碎片,
具體選擇哪種方式取決于虛擬機的垃圾回收策略,這種兩種記憶體方式參考了作業系統的記憶體分配策略,
記憶體分配的并發問題如何解決?
同步處理:利用CAS演算法(Compare And Swap)配上失敗重試機制,保證分配記憶體操作的原子性,
TLAB:Thread Local Allocation Buffer,每個執行緒先在執行緒的本地緩沖區中分配,本地快取區用完了再利用同步處理分配執行緒的本地快取區,
可以使用-XX:+/-UseTLAB來配置TLAB,
物件實體在堆中的記憶體分布布局?
物件頭、實體資料和對齊填充,
物件頭存盤著什么資訊?
一部分是用于存盤物件自身的運行時資料,如哈希碼、GC分代年齡、鎖狀態標志、執行緒持有的鎖(客戶端鎖定)、偏向執行緒ID、偏向時間戳等,
另一部分用于存盤型別指標,即物件指向它的型別元資料的指標,通過這個來確定物件是哪個類的實體,(反射會用到)如果是陣列,還會存盤陣列的長度,因為一般的物件實體通過元資料資訊已經可以確定大小,而陣列不行,
實體資料存盤著什么資訊?
程式代碼里所定義的各種型別的欄位內容,
為什么要對齊填充?
就和計網IP資料報以及計組里學的一樣,資料最好封裝成位元組的整數倍,方便CPU讀取,
物件如何進行訪問定位?
使用句柄
如果使用句柄訪問,在java堆中將劃分出一塊記憶體來作為句柄池,reference中存盤的就是物件的句柄地址,而句柄中包含物件實體資料與型別資料具體地址資訊,

直接訪問
使用直接訪問,在java堆中物件的記憶體布局就必須考慮如何放置訪問型別資料的相關資訊,reference中直接存盤物件地址即可,

句柄訪問和直接訪問的優缺點
句柄訪問在參考和具體資料之間增加了一層轉換關系,這層轉換關系使得物件在被移動的時候(如垃圾回收)只需要改變轉換關系,即改變句柄池中的參考指向即可,而參考本身不需要被修改,使用直接訪問最大的好處就是快,因為相對于句柄訪問減少了一次指標定位的時間,由于java是面向物件語言,物件訪問非常頻繁,因此這種訪問開銷積少成多也非常可可觀,hotspot虛擬機使用的就是直接訪問方式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/521787.html
標籤:Java
