運行時數據區結構圖
堆、棧、方法區的交互關係
html
《Java虛擬機規範》中明確說明:‘儘管全部的方法區在邏輯上屬於堆的一部分,但一些簡單的實現可能不會選擇去進行垃圾收集或者進行壓縮。’但對於HotSpotJVM而言,方法區還有一個別名叫作Non-heap(非堆),目的就是要和堆分開。
因此,==方法區能夠看做是一塊獨立於Java堆的內存空間。==java
public class MethodAreaDemo {
public static void main(String[] args) {
System.out.println("start...");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end...");
}
}
複製代碼
方法區在jdk7及jdk8的落地實現 git
在jdk8中,終於徹底廢棄了永久代的概念,改用與JRockit、J9同樣在本地內存中實現的元空間(Metaspace)來代替
github
元空間的本質和永久代相似,都是對JVM規範中方法區的實現。不過元空間與永久代最大的區別在於:==元空間再也不虛擬機設置的內存中,而是使用本地內存==面試
永久代、元空間並不僅是名字變了。內部結構也調整了算法
根據《Java虛擬機規範》得規定,若是方法區沒法知足新的內存分配需求時,將拋出OOM異常.數據庫
方法區的大小沒必要是固定的,jvm能夠根據應用的須要動態調整。
jdk7及之前:編程
jdk8及之後:windows
* jdk7及之前:
* 查詢 jps -> jinfo -flag PermSize [進程id]
* -XX:PermSize=100m -XX:MaxPermSize=100m
*
* jdk8及之後:
* 查詢 jps -> jinfo -flag MetaspaceSize [進程id]
* -XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m
複製代碼
如下代碼在JDK8環境下會報 Exception in thread "main" java.lang.OutOfMemoryError: Compressed class space 錯誤
數組
/**
* jdk6/7中:
* -XX:PermSize=10m -XX:MaxPermSize=10m
*
* jdk8中:
* -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
*
*/
public class OOMTest extends ClassLoader {
public static void main(String[] args) {
int j = 0;
try {
OOMTest test = new OOMTest();
for (int i = 0; i < 10000; i++) {
//建立ClassWriter對象,用於生成類的二進制字節碼
ClassWriter classWriter = new ClassWriter(0);
//指明版本號,修飾符,類名,包名,父類,接口
classWriter.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
//返回byte[]
byte[] code = classWriter.toByteArray();
//類的加載
test.defineClass("Class" + i, code, 0, code.length);//Class對象
j++;
}
} finally {
System.out.println(j);
}
}
}
複製代碼
對每一個加載的類型( 類class、接口interface、枚舉enum、註解annotation),JVM必 .須在方法區中存儲如下類型信息:
JVM必須保存全部方法的如下信息,同域信息同樣包括聲明順序:
如下代碼不會報空指針異常
public class MethodAreaTest {
public static void main(String[] args) {
Order order = null;
order.hello();
System.out.println(order.count);
}
}
class Order {
public static int count = 1;
public static final int number = 2;
public static void hello() {
System.out.println("hello!");
}
}
複製代碼
全局常量 static final 被聲明爲final的類變量的處理方法則不一樣,每一個全局常量在編譯的時候就被分配了。
代碼解析
Order.class字節碼文件,右鍵Open in Teminal打開控制檯,使用javap -v -p Order.class > tst.txt 將字節碼文件反編譯並輸出爲txt文件,能夠看到==被聲明爲static final的常量number在編譯的時候就被賦值了,這不一樣於沒有被final修飾的static變量count是在類加載的準備階段被賦值==
public static int count;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC
public static final int number;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: int 2
複製代碼
Public class Simpleclass {
public void sayhelloo() {
System.out.Println (hello) }
}
複製代碼
幾種在常量池內存儲的數據類型包括:
常量池,能夠看作是一張表,虛擬機指令根據這張常量表找到要執行的類名,方法名,參數類型、字面量等信息。
public class MethodAreaDemo {
public static void main(String[] args) {
int x = 500;
int y = 100;
int a = x / y;
int b = 50;
System.out.println(a + b);
}
}
複製代碼
main方法的字節碼指令
0 sipush 500
3 istore_1
4 bipush 100
6 istore_2
7 iload_1
8 iload_2
9 idiv
10 istore_3
11 bipush 50
13 istore 4
15 getstatic #2 <java/lang/System.out>
18 iload_3
19 iload 4
21 iadd
22 invokevirtual #3 <java/io/PrintStream.println>
25 return
複製代碼
"Exception in thread' dubbo client x.x connector’java.lang.OutOfMemoryError: PermGenspace"
而元空間和永久代之間最大的區別在於:==元空間並不在虛擬機中,而是使用本地內存。所以,默認狀況下,元空間的大小僅受本地內存限制==。jdk7中將StringTable放到了堆空間中。由於永久代的回收效率很低,在full gc的時候纔會觸發。而full GC 是老年代的空間不足、永久代不足時纔會觸發。這就致使了StringTable回收效率不高。而咱們開發中會有大量的字符串被建立,回收效率低,致使永久代內存不足。放到堆裏,能及時回收內存.
/**
* 《深刻理解Java虛擬機》中的案例:
* staticObj、instanceObj、localObj存放在哪裏?
*/
public class StaticObjTest {
static class Test {
static ObjectHolder staticObj = new ObjectHolder();
ObjectHolder instanceObj = new ObjectHolder();
void foo() {
ObjectHolder localObj = new ObjectHolder();
System.out.println("done");
}
}
private static class ObjectHolder {
}
public static void main(String[] args) {
Test test = new StaticObjTest.Test();
test.foo();
}
}
複製代碼
hsdb>scanoops 0x00007f32c7800000 0x00007f32c7b50000 JHSDB_ _TestCase$Obj ectHolder
0x00007f32c7a7c458 JHSDB_ TestCase$Obj ectHolder
0x00007f32c7a7c480 JHSDB_ TestCase$Obj ectHolder
0x00007f32c7a7c490 JHSDB_ TestCase$Obj ectHolder
複製代碼
有些人認爲方法區(如Hotspot,虛擬機中的元空間或者永久代)是沒有垃圾收集行爲的,其實否則。《Java 虛擬機規範》對方法區的約束是很是寬鬆的,提到過能夠不要求虛擬機在方法區中實現垃圾收集。事實上也確實有未實現或未能完整實現方法區類型卸載的收集器存在(如 JDK11 時期的 2GC 收集器就不支持類卸載)。
通常來講這個區域的回收效果比較難使人滿意,尤爲是類型的卸載,條件至關苛刻。可是這部分區域的回收有時又確實是必要的。之前 Sun 公司的 Bug 列表中,曾出現過的若干個嚴重的 Bug 就是因爲低版本的 Hotspot 虛擬機對此區域未徹底回收而致使內存泄漏。
方法區的垃圾收集主要回收兩部份內容:常量池中廢奔的常量和再也不使用的類型
三面:說一下JVM內存模型吧,有哪些區?分別幹什麼的?
Java8的內存分代改進
JVM內存分哪幾個區,每一個區的做用是什麼?
一面: JVM內存分佈/內存結構?棧和堆的區別?堆的結構?爲何兩個survivor區?
二面: Eden和Survior的比例分配
jvm內存分區,爲何要有新生代和老年代
二面: Java的內存分區
二面:講講jvm運行時數據庫區
何時對象會進入老年代?
JVM的內存結構,Eden和Survivor比例 。
JVM內存爲何要分紅新生代,老年代,持久代。新生代中爲何要分爲Eden和Survivor。
一面: Jvm內存模型以及分區,須要詳細到每一個區放什麼。
一面: JVM的內存模型,Java8作了什麼修改
JVM內存分哪幾個區,每一個區的做用是什麼?
java內存分配
jvm的永久代中會發生垃圾回收嗎?
一面: jvm內存分區,爲何要有新生代和老年代?
【代碼】
github.com/willShuhuan…
【筆記】
JVM_01 簡介
JVM_02 類加載子系統
JVM_03 運行時數據區1- [程序計數器+虛擬機棧+本地方法棧]
JVM_04 本地方法接口
JVM_05 運行時數據區2-堆
JVM_06 運行時數據區3-方法區
JVM_07 運行時數據區4-對象的實例化內存佈局與訪問定位+直接內存
JVM_08 執行引擎(Execution Engine)
JVM_09 字符串常量池StringTable
JVM_10 垃圾回收1-概述+相關算法
JVM_11 垃圾回收2-垃圾回收相關概念
JVM_12 垃圾回收3-垃圾回收器