1. 程式人生 > >【JAVA進階架構師指南】之二:JVM篇

【JAVA進階架構師指南】之二:JVM篇

## 前言   談到JAVA,就不得不提JVM---JAVA程式設計師繞不開的話題.也許有童鞋會說,我不懂JVM,但是我一樣可以寫出JAVA程式碼,我相信說這種話的童鞋,往往是隻有1-3年的初級開發人員,對JAVA理解還不深,不明白JVM的重要性,那接下來我們來說說,為什麼要學習JVM?   1.理解JVM,才能幫助我們寫出更好,更健壯的程式碼.舉個例子,以下程式碼的執行結果會是什麼呢?很多童鞋肯定會說:嗯?當我傻嗎?兩個不都是true嗎?這有啥好說的,真的是這樣嗎?感興趣的童鞋可以自己下來試一試,至於為什麼是這樣的結果,在下文會解釋清楚. ![file](https://img2020.cnblogs.com/other/1924225/202003/1924225-20200321025231753-1400154703.jpg)   2.理解JVM,可以幫助我們提升JAVA程式的效能,排除問題.   3.也是最重要的一點,面試必問! ## 虛擬機器的種類   我們知道,目前使用範圍最廣的虛擬機器是sun公司的HotSpot VM,在這之前,sun公司釋出的第一款虛擬機器是Sun Classic/Exact VM,這是世界上第一款商用虛擬機器.另外其他公司也有自己的虛擬機器,比如IBM J9 VM,Google Android Dalvik VM,Apache Harmony,Microsoft VM等待,但是使用範圍最廣的還是HotSpot. ## JVM記憶體劃分   引用一張圖來說明: ![file](https://img2020.cnblogs.com/other/1924225/202003/1924225-20200321025232074-211311758.jpg)   可以看到,JVM主要由方法區/堆區/虛擬機器棧/本地方法棧/程式計數器五個部分組成,從執行緒的角度來看,分為執行緒公有的部分(方發區/java堆)和執行緒私有的部分(虛擬機器棧/本地方法棧/程式計數器). ## 方法區   存放已經被虛擬機器載入的[類資訊/常量/靜態變數/即時編譯後的程式碼]等,有些文章也稱方法區為永久代,主要發生的異常是記憶體溢位:OutOfMemoryError.另外在JDK1.6版本中,常量池(這裡特指執行時常量池,我們一般說的常量池也都是指的執行時常量池)是存放於方法區中的(因此方法區可能會經常記憶體溢位),JDK1.7的時候常量池移到了JAVA堆(Heap)中,在JDK1.8的時候,已經沒有方法區了,取而代之的是一塊叫元資料(metaSpace)的空間. ## java堆   java堆主要存放的是物件例項以及陣列等資訊,主要發生的異常仍然是記憶體溢位:OutOfMemoryError.並且java堆區是GC重點關注的區域.另外,我們常說,幾乎所有的物件分配記憶體都是在java堆中進行,而不是說所有物件100%都在java堆中分配記憶體,是因為有兩種例外情況不會在java堆中分配記憶體,第一種是TLAB(執行緒本機分配快取),另一種是棧上分配,既然想成為一名架構師,童鞋們應該要弄明白什麼是TLAB和棧上分配,發揮你們的能力,盡情Google吧. ## 虛擬機器棧   java方法執行的記憶體模型,每個方法在執行的時候會封裝成一個棧幀,存放[區域性變量表/運算元棧/動態連結串列/方法出口]等資訊,方法的執行對應棧幀入棧和出棧的過程.棧的深度是有大小的,預設情況下棧的記憶體為1M,因此虛擬機器棧除了發生記憶體溢位異常,還有可能發生StackOverFlowError異常. ## 本機方法棧   和虛擬機器棧作用類似,區別在於本地方法棧儲存的是native方法的資訊. ## 程式計數器   當前執行緒執行的位元組碼行號指示器,是JVM中唯一一塊沒有記憶體溢位異常的區域. ## 常量池   接下來我們再倒回來看看,文章開頭的程式碼,執行結果會是什麼: ![file](https://img2020.cnblogs.com/other/1924225/202003/1924225-20200321025232347-154678034.jpg)   127返回的是true,128返回的確是false.為什麼?   首先我們知道,在java語言中 == 比較的是兩個物件的記憶體地址,只有equals方法才是比較兩個物件是否相等,執行結果告訴我們,值都為127的Integer a和b記憶體地址是相同的,他們是同一個物件,而值為128的Integer c和d的記憶體地址不同,他們是不同的兩個物件,那為什麼127就是相同的物件,128就是不同的物件呢?還記得上文中,我們說方法區中有一塊區域叫執行時常量池,存放的是各種常量,java語言對byte/short/char/int/string設定了常量池,比如我們檢視Integer的原始碼: ![file](https://img2020.cnblogs.com/other/1924225/202003/1924225-20200321025232620-2034723576.jpg)   可以發現,Integer的常量池範圍是-128~127,在該範圍內的Integer物件都會複用常量池中的值,因此a和b是相同物件,而超過該範圍,會重新new一個新的物件,因此c和d都是重新new出來的,地址當然不同,因此是false.另外String型別的常量池和前面四種類型不一樣,String型別的常量池是通過final來實現的.而float/double沒有常量池的概念,因為float和double本身都是科學技術法表示近似數,無法精確計算,存在精度丟失的情況,因此沒法為float和double建立常量池.   本文我們瞭解了JVM的記憶體區域,下一篇文章,讓我們來學習類載入機制,敬請期待!   如果覺得博主寫的不錯,歡迎關注博主微信公眾號,博主會不定期分享技術乾貨!   ![file](https://img2020.cnblogs.com/other/1924225/202003/1924225-20200321025233044-395423174.jpg) > 本文由部落格一文多發平臺 [OpenWrite](https://openwrite.cn?from=article_bottom)