前言 談到JAVA,就不得不提JVM JAVA程式員繞不開的話題.也許有童鞋會說,我不懂JVM,但是我一樣可以寫出JAVA代碼,我相信說這種話的童鞋,往往是只有1 3年的初級開發人員,對JAVA理解還不深,不明白JVM的重要性,那接下來我們來說說,為什麼要學習JVM? 1.理解JVM,才能幫助我們寫 ...
前言
談到JAVA,就不得不提JVM---JAVA程式員繞不開的話題.也許有童鞋會說,我不懂JVM,但是我一樣可以寫出JAVA代碼,我相信說這種話的童鞋,往往是只有1-3年的初級開發人員,對JAVA理解還不深,不明白JVM的重要性,那接下來我們來說說,為什麼要學習JVM?
1.理解JVM,才能幫助我們寫出更好,更健壯的代碼.舉個例子,以下代碼的執行結果會是什麼呢?很多童鞋肯定會說:嗯?當我傻嗎?兩個不都是true嗎?這有啥好說的,真的是這樣嗎?感興趣的童鞋可以自己下來試一試,至於為什麼是這樣的結果,在下文會解釋清楚.
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記憶體劃分
引用一張圖來說明:
可以看到,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中唯一一塊沒有記憶體溢出異常的區域.
常量池
接下來我們再倒回來看看,文章開頭的代碼,執行結果會是什麼:
127返回的是true,128返回的確是false.為什麼?
首先我們知道,在java語言中 == 比較的是兩個對象的記憶體地址,只有equals方法才是比較兩個對象是否相等,執行結果告訴我們,值都為127的Integer a和b記憶體地址是相同的,他們是同一個對象,而值為128的Integer c和d的記憶體地址不同,他們是不同的兩個對象,那為什麼127就是相同的對象,128就是不同的對象呢?還記得上文中,我們說方法區中有一塊區域叫運行時常量池,存放的是各種常量,java語言對byte/short/char/int/string設置了常量池,比如我們查看Integer的源碼:
可以發現,Integer的常量池範圍是-128~127,在該範圍內的Integer對象都會復用常量池中的值,因此a和b是相同對象,而超過該範圍,會重新new一個新的對象,因此c和d都是重新new出來的,地址當然不同,因此是false.另外String類型的常量池和前面四種類型不一樣,String類型的常量池是通過final來實現的.而float/double沒有常量池的概念,因為float和double本身都是科學技術法表示近似數,無法精確計算,存在精度丟失的情況,因此沒法為float和double創建常量池.
本文我們瞭解了JVM的記憶體區域,下一篇文章,讓我們來學習類載入機制,敬請期待!
如果覺得博主寫的不錯,歡迎關註博主微信公眾號,博主會不定期分享技術乾貨!
本文由博客一文多發平臺 OpenWrite 發佈!