對於 Java 程序員來講,在虛擬機自動內存管理機制下,再也不須要像C/C++程序開發程序員這樣爲內一個 new 操做去寫對應的 delete/free 操做,不容易出現內存泄漏和內存溢出問題。正是由於 Java 程序員把內存控制權利交給 Java 虛擬機,一旦出現內存泄漏和溢出方面的問題,若是不瞭解虛擬機是怎樣使用內存的,那麼排查錯誤將會是一個很是艱鉅的任務。java
線程私有的包括:程序計數器、虛擬機棧、本地方法棧程序員
線程共享的:堆、方法區、直接內存算法
記錄正在執行的虛擬機字節碼指令的地址。因爲是多線程,線程輪流切換,切換線程後爲了能恢復到正常的執行位置,每一個線程須要一個獨立的程序計數器。若是執行的是本地(Naive)方法,計數器爲空。此內存區域是惟一一個沒有規定任何OutOfMemoryError狀況的區域,它的生命週期隨着線程建立而建立,隨着線程結束而死亡。sql
Java 內存能夠粗糙的區分爲堆內存(Heap)和棧內存(Stack),其中棧就是如今說的虛擬機棧,或者說是虛擬機棧中局部變量表部分。數組
Java虛擬機棧也是線程私有的,生命週期與線程相同。描述的是Java方法執行的內存模型。每一個Java方法在執行的同時會建立一個棧幀用於存儲局部變量表、操做數棧、常量池引用、動態連接、程序出口等信息。每個方法從調用到執行完成的過程,對應一個棧幀在Java虛擬機棧中入棧和出棧的過程。緩存
局部變量表存放了編譯器可知的各類基本數據類型(boolean,byte,char,short,int,float,long,double)、對象引用(reference類型,不一樣於對象自己,多是一個指向對象起始地址的引用指針,也多是指向一個表明對象的句柄或其餘與此對象相關的位置)和returnAddressleixing (字節碼指令地址)。局部變量表所需內存在編譯期間完成分配,運行期間不會改變。多線程
能夠經過 -Xss 這個虛擬機參數來指定每一個線程的 Java 虛擬機棧內存大小: java -Xss 512M
架構
可能拋出的異常狀況:併發
本地方法棧與 Java 虛擬機棧相似,虛擬機棧爲虛擬機執行 Java 方法 (也就是字節碼)服務,而本地方法棧則爲虛擬機使用到的 Native 方法服務。有的虛擬機如HotSpot虛擬機把兩者合二爲一。拋出的異常與上述一致。分佈式
本地方法通常是用其它語言(C、C++ 或彙編語言等)編寫的,而且被編譯爲基於本機硬件和操做系統的程序,對待這些方法須要特別處理。
Java堆是整個虛擬機所管理的最大內存區域,全部的對象建立都是在這個區域進行內存分配,是被全部線程共享的一塊內存區域,在虛擬機啓動時建立,此內存區域的惟一目的就是存放對象實例,幾乎全部的對象實例以及數組都在這裏分配內存。
Java堆是垃圾收集器管理的主要區域(方法區也須要回收),所以又稱爲GC堆(Garbage Collected Heap)。如今收集器基本採用分代收集算法,能夠將堆分爲新生代和老年代。劃分的好處是能夠方便垃圾的準確回收。
Java堆能夠處於物理上不連續的內存空間中,只要邏輯上是連續的便可。堆還能夠動態增長其內存,當堆中沒法申請到新內存建立實例,而且堆也沒法再擴展時,將會拋出OutOfMemroyError。
能夠經過 -Xms 和 -Xmx 兩個虛擬機參數來指定一個程序的堆內存大小,第一個參數設置初始值,第二個參數設置最大值。
java -Xms1M -Xmx2M
方法區與 Java 堆同樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。方法區也是全部線程共享。方法區邏輯上屬於堆的一部分,可是爲了與堆進行區分,一般又叫「非堆」。
方法區和堆同樣不須要連續的內存,而且能夠動態擴展,動態擴展失敗同樣會拋出 OutOfMemoryError 異常。
在HotSpot虛擬機中,把方法區當作永久代來進行GC,對起回收的目標主要是針對常量池的回收以及對類型的卸載,可是通常比較難實現。垃圾收集行爲在這個區域是比較少出現的,但並不是數據進入方法區後就「永久存在」了。因爲方法區主要存儲類的相關信息,因此對於動態生成類的狀況比較容易出現永久代的內存溢出。在JDK1.8中,已經移除了永久代,用元空間來替代。元空間的本質和永久代相似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用本地內存。所以,默認狀況下,元空間的大小僅受本地內存限制。
運行時常量池是方法區中的一部分。Class 文件中除了有類的版本、字段、方法、接口等描述信息外,還有常量池信息(用於存放編譯期生成的各類字面量和符號引用)
既然運行時常量池時方法區的一部分,天然受到方法區內存的限制,當常量池沒法再申請到內存時會拋出 OutOfMemoryError 異常。JDK1.7及以後版本的 JVM 已經將運行時常量池從方法區中移了出來,在 Java 堆(Heap)中開闢了一塊區域存放運行時常量池。
直接內存並非虛擬機運行時數據區的一部分,也不是虛擬機規範中定義的內存區域,可是這部份內存也被頻繁地使用。並且也可能致使OutOfMemoryError異常出現。
JDK1.4中新加入的 NIO(New Input/Output) 類,引入了一種基於通道(Channel) 與緩存區(Buffer) 的 I/O 方式,它能夠直接使用Native函數庫直接分配堆外內存,而後經過一個存儲在 Java 堆中的 DirectByteBuffer 對象做爲這塊內存的引用進行操做。這樣就能在一些場景中顯著提升性能,由於避免了在 Java 堆和 Native 堆之間來回複製數據。
本機直接內存的分配不會收到 Java 堆的限制,可是,既然是內存就會受到本機總內存大小以及處理器尋址空間的限制。
歡迎工做一到五年的Java工程師朋友們加入Java架構開發: 855835163 羣內提供免費的Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!