根據JVM規範,最初Java內存分爲5個區域,分別爲堆(Heap)、JVM棧(Stack)、方法區(Method Area)、本地方法棧(Native Method Stack)、程序計數器(Program Counter Register)。方法區中,還有一塊運行時常量池(Runtime Constant Pool)區域。java
在JDK1.4版本新增的NIO特性中,新增了本地內存(Native Heap),此內存區域在JVM外分配,由OS管理,此區域又叫直接內存(Direct Memory),經過ByteBuffer
的靜態方法allocateDirect
可在本地內存中分配直接內存Buffer,並經過JVM堆中的DirectByteBuffer
對象引用此Buffer。服務器
方法區主要用於存儲類的元數據、運行時常量等數據,在JVM規範中,對於方法區的管理比較寬鬆,沒有明肯定義方法區的實現位置,JVM參考虛擬機HotSpot的實現上,把方法區放在堆的永久代中。多線程
隨着Spring等使用動態字節碼、反射、代理技術框架的流行,運行過程當中生成大量的類,方法區存儲的內容愈來愈多,致使方法區內存容易溢出,出現 java.lang.OutOfMemoryError: PremGen space
異常。框架
在JDK7開始對方法區結構進行了優化拆分,並在JDK8中完全去掉了方法區。優化方法包括把類的元信息、符號引用(Symbols)移到本地內存區域,把靜態變量(class static)、字面量(internal strings)移到堆區域。其中存儲類的元信息區域叫作元空間(Metaspace)。優化
區域 | 特徵 | 做用 | 配置參數 | 異常 | 生命週期 |
---|---|---|---|---|---|
堆 | 線程共享 | 類實例對象存儲空間 | -Xms -Xsx -Xmn | OutOfMemoryError | JVM |
棧 | 線程私有 | 線程棧幀,局部變量、方法參數、操做數、動態連接等信息 | -Xss(幀數) | StackOverflowError OutOfMemoryError | 線程 |
本地方法棧 | 線程私有 | 線程調用Native方法棧幀 | - | OutOfMemoryError | 線程 |
程序計數器 | 此區域很小,爲線程私有 | 記錄線程運行的字節碼行號等線程運行位置信息,用於線程的切換和恢復 | - | - | 線程 |
直接內存 | 在OS Native內存中分配,可突破JVM內存大小限制 | 經過相似mmap在OS內存中分配,可避免內存在OS和JVM間拷貝 | - | OutOfMemoryError | JVM |
元空間 | 在OS Native內存中分配 | 保存類的元信息、符號引用等信息 | XX:MetaspaceSize XX:MaxMetaspaceSize | - | JVM |
運行時常量池 | 在堆中分配 | 保存靜態變量、字面量等數據 | - | - | JVM |
每一個線程運行時,會在棧中分配一個線程棧,棧由棧幀(Stack Frame)組成,每次調用方法時會生成一個棧幀,方法返回時則彈出棧頂幀。棧中保存了方法入參、局部變量、操做數棧、動態連接、方法出口信息等數據。當一個線程棧幀的棧數量超過-Xss定義的幀數時,會拋出StackOverflowError異常,通常在遞歸調用時容易發生此錯誤。
ui
JDK8中,堆中移除了永生代區域,堆內存主要由新生代和老年代兩部分組成。其中新生代由一個伊甸園(Eden)和兩個倖存者(Survivor)3部分組成,新生代的垃圾回收頻率高,Minor GC時,把Eden和其中一個Survivor中的存活對象拷貝到另外一個Survivor區,並清除前面兩個區域的數據,經過這種結構和回收方式來提升垃圾回收效率,減小內存碎片。通過若干(默認15)次後還存活的對象,將進入老年代區,當老年代數據慢是會觸發Major GC。spa
老年代:新生代的內存大小默認比例爲2:1。Eden和兩個Survivor的比例爲8:1:1。內存的分配比例能夠經過 java -XX:+PrintFlagsFinal -version
命令進行查看。操作系統
[Global flags]
uintx InitialSurvivorRatio = 8
uintx NewRatio = 2
設置內存區域比例參數:線程
參數 | 說明 |
---|---|
-XX:InitialSurvivorRatio | 新生代Eden/Survivor空間的初始比例 |
-XX:Newratio | 老年代和新生代的內存比例 |
當前線程所執行的行號指示器。經過改變計數器的值來肯定下一條指令,好比循環,分支,跳轉,異常處理,線程恢復等都是依賴計數器來完成。設計
Java虛擬機多線程是經過線程輪流切換並分配處理器執行時間的方式實現的。爲了線程切換能恢復到正確的位置,每條線程都須要一個獨立的程序計數器,因此它是線程私有的。
java.lang.OutOfMemoryError:GC overhead limit exceeded
這是JDK6新增錯誤類型,當GC爲釋放很小空間佔用大量時間時拋出;通常是由於堆過小,致使異常的緣由,沒有足夠的內存。解決方案:
java.lang.OutOfMemoryError: unable to create new native thread
可能緣由是系統內存耗盡,沒法爲新線程分配內存或者建立線程數超過了操做系統的限制。經過兩個途徑解決: