在JVM的管控下,Java程序員再也不須要管理內存的分配與釋放,這和在C和C++的世界是徹底不同的。因此,在JVM的幫助下,Java程序員不多會關注內存泄露和內存溢出的問題。可是,一旦JVM發生這些狀況的時候,若是你不清楚JVM內存的內存管理機制是很難定位與解決問題的。程序員
Java虛擬機在運行時,會把內存空間分爲若干個區域,根據《Java虛擬機規範(Java SE 7 版)》的規定,Java虛擬機所管理的內存區域分爲以下部分:方法區、堆內存、虛擬機棧、本地方法棧、程序計數器。算法
方法區主要用於存儲虛擬機加載的類信息、常量、靜態變量,以及編譯器編譯後的代碼等數據。在jdk1.7及其以前,方法區是堆的一個「邏輯部分」(一片連續的堆空間),但爲了與堆作區分,方法區還有個名字叫「非堆」,也有人用「永久代」(HotSpot對方法區的實現方法)來表示方法區。數組
從jdk1.7已經開始準備「去永久代」的規劃,jdk1.7的HotSpot中,已經把本來放在方法區中的靜態變量、字符串常量池等移到堆內存中,(常量池除字符串常量池還有class常量池等),這裏只是把字符串常量池移到堆內存中;在jdk1.8中,方法區已經不存在,原方法區中存儲的類信息、編譯後的代碼數據等已經移動到了元空間(MetaSpace)中,元空間並無處於堆內存上,而是直接佔用的本地內存(NativeMemory)。根據網上的資料結合本身的理解對jdk1.3~1.六、jdk1.七、jdk1.8中方法區的變遷畫了張圖以下(若有不合理的地方但願讀者指出):markdown
去永久代的緣由有:性能
字符串存在永久代中,容易出現性能問題和內存溢出。spa
類及方法的信息等比較難肯定其大小,所以對於永久代的大小指定比較困難,過小容易出現永久代溢出,太大則容易致使老年代溢出。線程
永久代會爲 GC 帶來沒必要要的複雜度,而且回收效率偏低。3d
堆內存主要用於存放對象和數組,它是JVM管理的內存中最大的一塊區域,堆內存和方法區都被全部線程共享,在虛擬機啓動時建立。在垃圾收集的層面上來看,因爲如今收集器基本上都採用分代收集算法,所以堆還能夠分爲新生代(YoungGeneration)和老年代(OldGeneration),新生代還能夠分爲 Eden、From Survivor、To Survivor。code
程序計數器是一塊很是小的內存空間,能夠看作是當前線程執行字節碼的行號指示器,每一個線程都有一個獨立的程序計數器,所以程序計數器是線程私有的一塊空間,此外,程序計數器是Java虛擬機規定的惟一不會發生內存溢出的區域。orm
虛擬機棧也是每一個線程私有的一塊內存空間,它描述的是方法的內存模型,直接看下圖所示:
虛擬機會爲每一個線程分配一個虛擬機棧,每一個虛擬機棧中都有若干個棧幀,每一個棧幀中存儲了局部變量表、操做數棧、動態連接、返回地址等。一個棧幀就對應 Java 代碼中的一個方法,當線程執行到一個方法時,就表明這個方法對應的棧幀已經進入虛擬機棧而且處於棧頂的位置,每個 Java 方法從被調用到執行結束,就對應了一個棧幀從入棧到出棧的過程。
本地方法棧與虛擬機棧的區別是,虛擬機棧執行的是 Java 方法,本地方法棧執行的是本地方法(Native Method),其餘基本上一致,在 HotSpot 中直接把本地方法棧和虛擬機棧合二爲一,這裏暫時不作過多敘述。
上面說到,jdk1.8 中,已經不存在永久代(方法區),替代它的一塊空間叫作 「 元空間 」,和永久代相似,都是 JVM 規範對方法區的實現,可是元空間並不在虛擬機中,而是使用本地內存,元空間的大小僅受本地內存限制,但能夠經過 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 來指定元空間的大小。
JVM內存區域劃分,便於它可以更加高效的管理自身的內存。當程序中出現這種因爲JVM形成的內存溢出的狀況的時候,須要根據不一樣的狀況作不一樣的分析與處理。