JAVA記憶體區域與記憶體溢位異常
正文
一. 基本概念
在開始講解之前, 需要先明確關於
JVM
的一些基本概念
我們都知道,
Java
是一個跨平臺的語言,
Java
跨平臺的基本支撐其實就是
JVM
對作業系統底層細節的遮蔽, 相當於加了一箇中間層(計算機中的任何問題都可以加一箇中間層解決~),
Java
不再像
C/C++
等語言一樣直接翻譯為針對特殊平臺的機器碼, 而是翻譯為位元組碼, 也即是我們的
class
檔案, 下圖大概可以比較簡明的概括了~; 位元組碼就相當於
Java
世界中的彙編, 而
JVM
則不是跨平臺的, 只是不同平臺的
JVM
都能識別和執行標準格式的位元組碼檔案而已
關於
JVM
執行
class
檔案, 我覺得下圖已經可以比較準確的表達了
我們下面要講的就是
Runtime Data Area
部分
二. 執行時資料區
JVM
會在執行
Java
程式的時候把它所管理的記憶體劃分為若干個不同的資料區, 如下:
2.1 程式計數器
執行緒私有
2.1.1 儲存資料型別
指向下一條需要執行的位元組碼指令; 如果執行緒正在執行一個
Java
方法, 該計數器記錄的是正在執行的虛擬機器位元組碼指令的地址; 如果正在執行
Native
方法, 該計數器值則為空(
Undefined
)
2.1.2 異常情況
該區域是是唯一一個在
Java
虛擬機器中沒有規定任何
OutOfMemoryError
情況的區域
2.2 Java虛擬機器棧
執行緒私有
2.2.1 儲存資料型別
描述
Java
方法執行的記憶體模型, 每個方法呼叫就對應著一個棧幀的入棧和出棧; 一個棧幀裡面儲存了區域性變數表, 運算元棧, 動態連結, 方法出口等資訊
區域性變數表儲存了編譯器可知的各種基本資料型別, 物件引用,
returnAddress
; 區域性變數表的大小在編譯期間即可確定, 執行期間大小不變
2.2.2 異常情況
-
StackOverflowError : 執行緒請求棧深度大於虛擬機器允許深度
異常示例程式碼:
public class JavaVMStackSOF { private int stackLength = 1; public void stackLeak() { stackLength++; stackLeak(); } public static void main(String[] args) { JavaVMStackSOF sof = new JavaVMStackSOF(); try { sof.stackLeak(); } catch (Throwable e) { System.out.println("Stack Length: " + sof.stackLength); throw e; } } }
-
OutOfMemoryError : 虛擬機器棧動態擴充套件時無法申請到足夠記憶體
異常示例程式碼:
public class JavaVMStackOOM { private void dontStop() { while (true) { } } public void stackLeakByThread() { while (true) { new Thread(new Runnable() { @Override public void run() { dontStop(); } }).start(); } } public static void main(String[] args) { JavaVMStackOOM oom = new JavaVMStackOOM(); oom.stackLeakByThread(); } }
注: 由於作業系統分配給每個程式的記憶體空間是有限制的, 所以如果是由於建立過多的執行緒導致記憶體溢位, 在不能減少執行緒數或者更換
64
位虛擬機器的情況下, 可以選擇透過減少最大堆和減少棧容量來換取更多的執行緒
2.3 本地方法棧
執行緒私有
2.3.1 儲存資料型別
和虛擬機器棧類似, 只是本地方法棧提供的是
Native
方法服務
2.3.2 異常情況
StackOverflowError
和
OutOfMemoryError
2.4 Java堆
-
執行緒共享
-
垃圾收集管理的主要區域
2.4.1 儲存資料型別
幾乎所有的物件例項都在這裡分配
2.4.2 異常情況
OutOfMemoryError
異常示例:
public class JavaVMHeapOOM { static class HeapOOM { } public static void main(String[] args) { List<HeapOOM> list = new ArrayList(); while (true) { list.add(new HeapOOM()); } } }
2.5 方法區
-
執行緒共享
-
該區域的垃圾回收目標主要是針對常量池的回收和對型別的解除安裝
2.5.1 儲存資料型別
儲存已被虛擬機器載入的類資訊, 常量, 靜態變數, 即使編譯器編譯後的程式碼等資料
2.5.2 執行時常量池
執行時常量池是方法區的一部分, 但是
JDK6
之後, 常量池被放入了堆中;
Class
檔案中也有常量池部分, 即編譯期生成的各種字面量和符號引用, 這部分將在類載入後進入方法區的執行時常量池中, 此外還會把翻譯出來的直接引用也儲存在執行時常量池中
執行時常量池相對於
Class
檔案常量池的另外一個最重要的特徵是具備動態性, 即執行期間也可以將新的常量放入池中, 比如
String
的
intern()
方法
String.intern()
作用是: 如果字串常量池中已經包含一個等於此
String
物件的字串, 則返回代表池中這個字串的
String
物件; 否則, 將此
String
物件包含的字串新增到常量池中, 並且返回此
String
物件的引用
同樣, 收方法區的限制, 當常量池無法再申請到記憶體時會丟擲
OutOfMemoryError
2.5.3 異常情況
OutOfMemoryError
: 方法區無法滿足記憶體分配需求
異常示例:
public class RuntimeConstantPoolOOM { public static void main(String[] args) { List<String> list = new ArrayList<>(); int i = 0; while (true) { list.add(String.valueOf(i++).intern()); } } }
2.6 直接記憶體
直接記憶體不是虛擬機器執行時資料區的一部分, 但是也被頻繁使用, 如: 在
JDK1.4
中新加入了
NIO
類, 引入了一種基於通道(
Chanel
)和緩衝區(
Buffer
)的
I/O
方式, 它可以使用
Native
函式庫直接分配堆外記憶體, 然後透過一個儲存在
Java
堆中的
DirectByteBuffer
物件作為這塊記憶體的引用進行操作, 避免了在
Java
堆和
Native
堆中來回複製資料, 提高效能
同樣會產生
OutOfMemoryError
三. 常見問題
注: 下文摘自文末參考連結, 權作個人筆記~
-
程式執行永遠都是在棧中進行的,因而引數傳遞時, 只存在傳遞基本型別和物件引用的問題, 不會直接傳物件本身; 但是傳引用的錯覺是如何造成的呢? 在執行棧中, 基本型別和引用的處理是一樣的, 都是傳值, 所以, 如果是傳引用的方法呼叫, 也同時可以理解為“傳引用值”的傳值呼叫, 即引用的處理跟基本型別是完全一樣的; 但是當進入被呼叫方法時, 被傳遞的這個引用的值, 被程式解釋(或者查詢)到堆中的物件, 這個時候才對應到真正的物件; 如果此時進行修改; 修改的是引用對應的物件; 而不是引用本身; 即: 修改的是堆中的資料; 所以這個修改是可以保持的了
-
我產生的物件不多呀, 為什麼還會產生
OutOfMemory
?
答: 你繼承層次忒多了,
Heap
中產生的物件是先產生父類, 然後才產生子類, 明白不?
-
OutOfMemory
錯誤分幾種? 答: 分兩種, 分別是OutOfMemoryError:java heap size
和OutOfMemoryError: PermGen space
, 兩種都是記憶體溢位,heap size
是說申請不到新的記憶體了, 這個很常見,檢查應用或調整堆記憶體大小;PermGenspace
是因為永久儲存區滿了, 這個也很常見, 一般在熱釋出的環境中出現, 是因為每次釋出應用系統都不重啟, 久而久之永久儲存區中的死物件太多導致新物件無法申請記憶體, 一般重新啟動一下即可 -
為什麼不建議在程式中顯式的生命
System.gc()
? 答: 因為顯式宣告是做堆記憶體全掃描, 也就是Full GC
, 是需要停止所有的活動的(Stop The World Collection
), 你的應用能承受這個嗎?
如果你想學好JAVA這門技術,也想在IT行業拿高薪,可以參加我們的訓練營課程,選擇最適合自己的課程學習,技術大牛親授,8個月後,進入名企拿高薪。我們的課程內容有:Java工程化、高效能及分散式、高效能、深入淺出。高架構。效能調優、Spring,MyBatis,Netty原始碼分析和大資料等多個知識點。如果你想拿高薪的,想學習的,想就業前景好的,想跟別人競爭能取得優勢的,想進阿里面試但擔心面試不過的,你都可以來,q群號為:180705916 進群免費領取學習資料。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31553506/viewspace-2215099/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- JVM(2)-Java記憶體區域與記憶體溢位異常JVMJava記憶體溢位
- Java記憶體區域與記憶體溢位異常 - 執行時資料區Java記憶體溢位
- Java記憶體區域與記憶體溢位異常(JVM學習系列1)Java記憶體溢位JVM
- 第二章 Java記憶體區域與記憶體溢位異常(1)Java記憶體溢位
- JVM學習-02-Java記憶體區域與記憶體溢位異常JVMJava記憶體溢位
- 深入理解Java虛擬機器-Java記憶體區域與記憶體溢位異常Java虛擬機記憶體溢位
- 深入理解JVM讀書筆記一: Java記憶體區域與記憶體溢位異常JVM筆記Java記憶體溢位
- Java虛擬機器01——Java記憶體資料區域和記憶體溢位異常Java虛擬機記憶體溢位
- 深入理解JVM之記憶體區域與記憶體溢位JVM記憶體溢位
- Java記憶體溢位(OOM)異常完全指南Java記憶體溢位OOM
- 【深入Java虛擬機器】之一:Java記憶體區域與記憶體溢位Java虛擬機記憶體溢位
- Java棧溢位|記憶體洩漏|記憶體溢位Java記憶體溢位
- Java 常見記憶體溢位異常與程式碼實現Java記憶體溢位
- Java記憶體溢位Java記憶體溢位
- java記憶體溢位和記憶體洩漏的區別Java記憶體溢位
- JVM記憶體區域以及各區域的記憶體溢位異常,記憶體分代策略,垃圾收集演算法,各種垃圾收集器JVM記憶體溢位演算法
- JVM——記憶體洩漏與記憶體溢位JVM記憶體溢位
- java 程式記憶體溢位Java記憶體溢位
- 深入理解JVM虛擬機器-JVM記憶體區域與記憶體溢位JVM虛擬機記憶體溢位
- 記憶體溢位記憶體溢位
- JVM系列之實戰記憶體溢位異常JVM記憶體溢位
- Java記憶體區域和記憶體模型Java記憶體模型
- [Java基礎]記憶體洩漏和記憶體溢位Java記憶體溢位
- 記憶體溢位和記憶體洩露記憶體溢位記憶體洩露
- 記憶體洩漏和記憶體溢位記憶體溢位
- Java記憶體溢位情況Java記憶體溢位
- 記憶體洩漏與記憶體溢位神比較記憶體溢位
- Java記憶體區域Java記憶體
- 【記憶體洩漏和記憶體溢位】JavaScript之深入淺出理解記憶體洩漏和記憶體溢位記憶體溢位JavaScript
- JBOSS記憶體溢位記憶體溢位
- 【轉】java中的記憶體溢位和記憶體洩漏Java記憶體溢位
- JVM面試問題系列:深入詳解JVM 記憶體區域及記憶體溢位分析JVM面試記憶體溢位
- Java記憶體區域與分配策略Java記憶體
- 異常、堆記憶體溢位、OOM的幾種情況記憶體溢位OOM
- JavaScript之記憶體溢位和記憶體洩漏JavaScript記憶體溢位
- WebLogic: 記憶體溢位Web記憶體溢位
- 記憶體溢位問題記憶體溢位
- 記憶體溢位的分析記憶體溢位