Java虛擬機在執(zhí)行java程序的過程中會把它所管理的內(nèi)存化分為若干個不同的數(shù)據(jù)區(qū)域。這些區(qū)域有各自的用途,以及創(chuàng)建和銷毀的時間。有些區(qū)域隨之虛擬機進程的啟動而一直存在,有些區(qū)域則依賴用戶線程的啟動和結束而建立和銷毀。

1. 程序計數(shù)器 Program Counter Register
1.1 定義
<font color="green">作用:記住下一條jvm指令的執(zhí)行地址。</font>
<font color="green">特點:</font>
- 線程私有
- 不會存在內(nèi)存溢出
<font color="green">為了線程切換后能恢復到正確的執(zhí)行位置,每條線程都要有一個獨立的程序計數(shù)器,各線程之間的計數(shù)器互不影響,獨立存儲。所以這部分是線程私有的。 </font>
2. 虛擬機棧 Java Virtual Machine Stacks
2.1 定義
- 每個線程運行時所需要的內(nèi)存稱為虛擬機棧
- 每個棧由多個棧幀組成,棧幀:每個方法執(zhí)行所占用的內(nèi)存
- 棧幀用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息
- 一個方法從調(diào)用到執(zhí)行完畢的過程,都對應著一個棧幀在虛擬機棧中從入棧到出棧的過程
- 每個線程只能有一個活動棧幀,對應著當前正在執(zhí)行的那個方法
<font color="green">局部變量表</font>
存放方法參數(shù)和方法內(nèi)部定義的局部變量。
存放編譯期可知的各種基本數(shù)據(jù)類型、引用數(shù)據(jù)類型、return Address類型
局部變量表的內(nèi)存空間在編譯期完成分配,當進入一個方法時,這個方法需要在幀分配多少內(nèi)存是固定的。
在方法運行期間是不會改變局部變量表的大小的。
<font color="orage">問題辨析:</font>
1.垃圾回收是否涉及棧內(nèi)存?
2.棧內(nèi)存分配越大越好嗎?
3.方法內(nèi)的局部變量是否線程安全?
- 如果方法內(nèi)局部變量沒有逃離方法作用域,它是線程安全的
2.2 棧內(nèi)存溢出
- 棧幀過多導致內(nèi)存溢出
- 棧幀過大導致內(nèi)存溢出
2.3 線程運行診斷
案例1:cpu占用過多
用top定位哪個進程對cpu的占用過高
ps H -eo pid,tid,%cpu | grep 進程id (用ps進一步定位是哪個線程引起的)
-
jstack 進程id
- 找到有問題的線程,定位問題代碼的源碼位置
3. 本地方法棧
本地方法棧為虛擬機使用到的native方法服務。
4. 堆 Heap
4.1 定義
- 通過new 關鍵字,創(chuàng)建對象都會使用堆內(nèi)存
<font color="green"> 特點:</font>
- 它是線程共享的,堆中的對象都需要考慮線程安全問題
- 有垃圾回收機制
4.2 堆內(nèi)存溢出
4.3 堆內(nèi)存診斷
-
jps工具
查看當前系統(tǒng)有哪些java進程
-
jmap
查看堆內(nèi)存占用情況 jmap -head 進程id
-
jconsole
一個可視化工具
5. 方法區(qū) Method Area
5.1 定義
5.2 組成

5.3 方法區(qū)內(nèi)存溢出
- 1.8 以前會導致永久代內(nèi)存溢出
java.lang.OutOfMemeryError: PermGen space
-XX:MaxPermSize=8m
- 1.8 以后會導致元空間內(nèi)存溢出
java.lang.OutOfMemeryError: Metaspace
-XX:MaxMetaspaceSize=8m
5.4 運行時常量池
常量池就是一張表,虛擬機指令根據(jù)這張常量表找到要執(zhí)行的類名、方法名、參數(shù)類型、字面量等信息
-
運行時常量池,常量池是 *.class 文件中的。當該類被加載,它的常量池信息就會放入運行時常量池中。
并把里面的符號地址變?yōu)檎鎸嵉牡刂?/p>
5.5 StringTable
先來看一段代碼
String s1 = "a";
String s2 = "b";
String s3 = "a" + "b";
String s4 = s1 + s2;
String s5 = "ab";
String s6 = s4.intern();
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
String x2 = new String("c") + new String("d");
String x1 = "cd";
x2.intern();
//如果調(diào)換了最后兩行代碼的位置呢?如果是jdk 1.6呢?
System.out.println(x2 == x1);
5.6 StringTable特性
- 常量池中的字符串僅是符號,第一次使用時才變成對象
- 利用串池的機制,來避免重復創(chuàng)建字符串對象
- 字符串變量的拼接原理是 StringBuilder(1.8)
- 字符串常量拼接的原理是編譯器優(yōu)化
- 可以使用intern()方法,主動將串池中還沒有的字符串對象放入串池
- 1.8 將這個字符串對象嘗試放入串池,如果有則并不會放入,如果沒有則會放入串池,會把串池中的對象返回。
- 1.6 將這個字符串對象嘗試放入串池,如果有則并不會放入,如果沒有會把此對象復制一份,放入串池,會把串池中的對象返回