jvm 入門篇,想要學習jvm,必須先得了解JVM內存模型,JVM內存模型,JVM內存模型,JVM內存模型,JVM內存模型。重要的事情說多遍。java
java虛擬機按照運行時內存使用區域劃分如圖:web
區域 | 是否線程共享 | 是否會內存溢出 |
---|---|---|
程序計數器 | 否 | 不會 |
java虛擬機棧 | 否 | 會 |
本地方法棧 | 否 | 會 |
堆 | 是 | 會 |
方法區 | 是 | 會 |
程序計數器就是記錄當前線程執行程序的位置,改變計數器的值來肯定執行的下一條指令,好比循環、分支、方法跳轉、異常處理,線程恢復都是依賴程序計數器來完成。
Java虛擬機多線程是經過線程輪流切換並分配處理器執行時間的方式實現的。爲了線程切換能恢復到正確的位置,每條線程都須要一個獨立的程序計數器,因此它是線程私有的。
若是線程正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;若是正在執行的是Native方法,這個計數器值則爲空(Undefined)。此內存區域是惟一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError狀況的區域。算法
java虛擬機棧是線程私有,生命週期與線程相同。建立線程的時候就會建立一個java虛擬機棧。
虛擬機執行java程序的時候,每一個方法都會建立一個棧幀,棧幀存放在java虛擬機棧中,經過壓棧出棧的方式進行方法調用。
棧幀又分爲一下幾個區域:局部變量表、操做數棧、動態鏈接、方法出口等。
平時咱們所說的變量存在棧中,這句話說的不太嚴謹,應該說局部變量存放在java虛擬機棧的局部變量表中。
java的8中基本類型的局部變量的值存放在虛擬機棧的局部變量表中,若是是引用型的變量,則只存儲對象的引用地址。服務器
本地方法棧 爲虛擬機使用到本地方法服務(native)。本地方法棧爲線程私有,功能和虛擬機棧很是相似。線程在調用本地方法時,來存儲本地方法的局部變量表,本地方法的操做數棧等等信息。多線程
本地方法:是非java語言實現的方法,例如,java調用C語言,來操做某些硬件信息。併發
堆是被全部線程共享的區域,實在虛擬機啓動時建立的。堆裏面存放的都是對象的實例(new 出來的對象都存在堆中)。
咱們日常所說的垃圾回收,主要回收的就是堆區。爲了提高垃圾回收的性能,又把堆分紅兩塊區新生代(young)和年老代(old),更細一點劃分新生代又可劃分爲Eden區和2個Survivor區(From Survivor和To Survivor)。
以下圖結構:jvm
方法區是被全部線程共享區域,用於存放已被虛擬機加載的類信息,常量,靜態變量等數據。被Java虛擬機描述爲堆的一個邏輯部分。習慣是也叫它永久代(permanment generation)
永久代也會垃圾回收,主要針對常量池回收,類型卸載(好比反射生成大量的臨時使用的Class等信息)。
常量池用於存放編譯期生成的各類字節碼和符號引用,常量池具備必定的動態性,裏面能夠存放編譯期生成的常量;運行期間的常量也能夠添加進入常量池中,好比string的intern()方法。
當方法區滿時,沒法在分配空間,就會拋出內存溢出的異常(OutOfMemoneyError)。
java8中已經沒有方法區了,取而代之的是元空間(Metaspace)。函數
直接內存(Direct Memory)並非虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域,可是這部份內存也被頻繁地使用,並且也可能致使OutOfMemoryError異常出現。
JDK1.4加的NIO中,ByteBuffer有個方法是allocateDirect(int capacity) ,這是一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,它可使用Native函數庫直接分配堆外內存,而後經過一個存儲在Java堆裏面的DirectByteBuffer對象做爲這塊內存的引用進行操做。這樣能在一些場景中顯著提升性能,由於避免了在Java堆和Native堆中來回複製數據。性能