文本已收錄至個人GitHub倉庫,歡迎Star:github.com/bin39232820…
種一棵樹最好的時間是十年前,其次是如今
我知道不少人不玩qq了,可是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵你們在技術的路上寫博客java
前面的基礎寫完了,接下來也是很重要的一部分,把數據加載到內存中,每種數據加載到哪一個位置呢?git
對於 C C++ 來講,在內存管理領域,他們既擁有最高的權利的皇帝,可是同時他們又是從事最基礎工做的勞動人員,由於他們擔負着每個對象從開始到結束的維護責任,程序員
對於Java來講,再虛擬機自動內存管理的幫助下,再也不須要爲每個new操做去分配內存,不容易出現內存泄漏和內存溢出的狀況,可是由於咱們Java程序員 不用管理內存,因此一旦出現內存問題,很容易讓咱們手忙腳亂,因此呢咱們必需要了解Java虛擬器的內存管理機制,以便咱們能更好的處理各類各樣的問題github
Java虛擬機在執行Java程序的過程當中會把所管理的內存劃分爲若干個不一樣的數據區域,這些區域都有各自的用途,以及建立和銷燬時間。再Java 1.8中 從宏觀上來講分爲線程共享,和線程私有 主要是分爲如下幾個區域算法
特色:線程內存獨享,佔用內存小,生命週期與線程相同(隨線程誕生而誕生,隨線程消亡而消亡)數組
功能:當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復(cpu在不斷輪詢執行任務)等基礎功能都須要依賴這個計數器來完成安全
異常:該區域沒有定義異常markdown
特色:先進後出,線程內存獨享,生命週期與線程相同多線程
單位:棧幀jvm
功能:已先進後出執行方法體的方法,執行完成的棧幀出棧
例子
虛擬機壓棧的過程
結論
接下來咱們來談談棧的基本單位棧幀吧
只有虛擬機棧頂的棧幀纔是有效的,稱爲當前棧幀 (Current Stack Frame),這個棧幀所關聯的方法稱爲當前方法(Current Method) 組成:
做用:用來存儲方法中的局部變量
基本單位:slot
直接引用 vs 使用句柄池
直接引用
reference直接指向對象,對象中指向對象類型數據
優勢:速度快,節約指針開銷。HotSpot採用的主要方式
使用句柄池:
java堆中會維護一個句柄池,句柄池分別指向對象實例(堆)的和對象類型數據(方法區)
優勢:對象移動後只需改變句柄池的指向地址,而不須要改變引用的指向地址。穩定
其實用白話來講 就是2我的是直接本身單線聯繫,仍是經過一個第三方聯繫,本身並不知道本身要聯繫的是誰,這個再抗戰特務劇中很常見呀。
操做數棧的深度在編譯器就能夠肯定其大小了,所以在程序執行期間局部變量表的大小是不會改變的。
功能:實現程序功能
補充下直接引用與符號引用
靜態解析:符號引用一部分會在類加載階段或第一次使用的時候轉化爲直接引用
動態鏈接: 將在每一次的運行期期間轉化爲直接引用
當一個方法執行完畢以後,要返回以前調用它的地方,所以在棧幀中必須保存一個方法返回地址。
特色:存儲對象,線程間內存共享,佔用大量內存,垃圾回收關注的重點區域
異常:OutOfMemoryError
每次都向堆中存放對象,方法結束後,銷燬棧幀的局部變量表時同時銷燬引用,該對象就成了可回收的垃圾。咋看起來沒什麼不對呀,但是仔細思考下仍是存在兩個問題 1.不斷的來回增長刪除對象,對於GC的工做量太大。 2.java使指針碰撞(堆中存入新對象的時候,指針根據對象大小移動到相應位置)來爲對象分配內存。若是在多線程的環境下,就會出現兩個對象同時移動當前前指針的狀況,形成線程不安全的狀況。
這裏就要引入TLAB的概念了
TLAB的全稱是Thread Local Allocation Buffer,這是一個線程專用的內存分配區域。每一個線程都會從Eden分配一塊空間,當線程銷燬時,咱們天然能夠回收掉TLAB的內存。
使用TLAB指令 -XX:UseTLAB
優勢:線程安全,減小垃圾回收的壓力。
缺點:TLAB空間大小是固定的,面對大對象的時候不夠靈活
特色:存儲類,線程間內存共享
存放已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據
異常:OutOfMemoryError
提到方法區不得不說的就是運行時常量池
補充:方法區不是永久代,只是Hotspot的實現方式而已。
運行時常量池是方法區的一部分,Class文件除了有類的版本,字段,方法,還有常量池
Java虛擬機對class文件每一部分的格式都有嚴格規定,每個字節用於存儲哪一種數據都必須符合規範纔會被jvm承認。但對於運行時常量池,Java虛擬機規範沒作任何細節要求。
運行時常量池有個重要特性是動態性,Java語言不要求常量必定只在編譯期才能產生,也就是並不是預置入class文件中常量池的內容才能進入方法區的運行時常量池,運行期間也有可能將新的常量放入池中,這種特性使用最多的是String類的intern()方法。
既然運行時常量池是方法區的一部分,天然受到方法區內存的限制。當常量池沒法再申請到內存時會拋出outOfMemeryError異常。
### 當虛擬機遇到一條New指令時:會進行以下步驟
指針碰撞:假設Java堆中的內存是絕對規整的,全部用過的內存都放在一邊,空閒的內存放在另外一邊。中間放着一個指針做爲分界點的指示器,分配內存就僅僅是把指針往空閒空間那邊挪動一段與對象大小相等的距離。這種方式則屬於指針碰撞。
空閒列表:若是堆中的內存並非規整的,已使用的內存和空閒內存相互交錯,顯然沒法使用指針碰撞。虛擬機就必須維護一個列表,記錄哪些內存是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給對象實例,並更新記錄表上的數據。這種方式屬於空閒列表。
具體選擇哪一種分配方式由Java堆決定,而Java堆是否規整,則有GC收集器決定。所以使用Serial、ParNew等帶Compact過程的收集器時,系統採用的分配算法是指針碰撞。而使用CMS這種基於Mark-Sweep算法的收集器時,一般採用的空閒列表。
在HotSpot虛擬機中,對象在內存中的佈局能夠分爲3塊區域:對象頭,實例數據和對齊填充
對象頭包括兩部分信息:
對齊填充:
明天就是 咱們大頭 垃圾回收算法,和垃圾回收器了,而後咱們再搞幾個實戰,對於JVM也算是有一個基礎的認識了,以後就要靠你們本身多去累計實戰經驗了。
好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是真粉。
創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見
六脈神劍 | 文 【原創】若是本篇博客有任何錯誤,請批評指教,不勝感激 !