這是Oracle官方對jvm內存的定義:docs.oracle.com/javase/spec…html
經過以下代碼生成class文件,詳細分析一下在棧裏面對象是怎麼劃分的:java
public class Math {
public void compute() {
int a = 10;
int b = 5;
int c = (a + b) * 100;
System.out.println(c);
}
public static void main(String[] args) {
Math math = new Math();
math.compute();
}
}
複製代碼
執行 javap -c Math.class > Math.txt
對class文件進行反編譯並輸出到Math.txt文件裏面詳細看一下:web
Compiled from "Math.java"
public class com.demo.jvm.Math {
public com.demo.jvm.Math();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void compute();
Code:
0: bipush 10
2: istore_1
3: iconst_5
4: istore_2
5: iload_1
6: iload_2
7: iadd
8: bipush 100
10: imul
11: istore_3
12: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
15: iload_3
16: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
19: return
public static void main(java.lang.String[]);
Code:
0: new #4 // class com/demo/jvm/Math
3: dup
4: invokespecial #5 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #6 // Method compute:()V
12: return
}
複製代碼
反編譯以後的結果就是jvm的指令碼,每條指令碼都有它特定的含義算法
咱們對compute()方法的指令一條一條的分析:markdown
bipush 10
將一個8位帶符號整數壓入棧:把10放入操做數棧;istore_1
將int類型值存入局部變量1:把操做數棧裏面的值放到局部變量表下標爲1的位置(這裏爲何是下標1而不是0呢?當棧幀被建立出來的時候,局部變量表中會默認存放一個this的引用,下標是0);iconst_5
將int類型常量5壓入棧:和第1步的意思同樣,把5放入操做數棧;istore_2
將int類型值存入局部變量2:把操做數棧裏面的5放到局部變量表的下標爲2的位置;iload_1
從局部變量1中裝載int類型值:把局部變量表中的10拿出來放到操做數棧裏面;iload_2
從局部變量2中裝載int類型值:把局部變量表中的5拿出來放到操做數棧裏面;iadd
執行int類型的加法:把操做數棧裏面的值加起來(10 + 5)= 15;bipush 100
將一個8位帶符號整數壓入棧:把100放入操做數棧;imul
執行int類型的乘法:把操做數棧裏面的值乘起來(15 * 100)= 1500;istore_3
將int類型值存入局部變量3:把操做數棧的1500放到局部變量表下標3的位置;getstatic #2
從類中獲取靜態字段:這行指令是獲取PrintStream
對象的,就是System.out那段代碼,後面的#2
是符號引用,在常量池裏面維護;iload_3
從局部變量3中裝載int類型值:把1500從局部變量表3的位置拿出來放到操做數棧裏面;invokevirtual #3
調度對象的方法:調用#3
引用的對象的方法,#3也是符號引用,實際就是調用PrintStream.println
參數就是操做數棧裏面的1500;return
方法執行完畢,return結束;每一個方法都對應一個棧幀,若是無限遞歸則會致使StackOverFlowError
,棧的默認大小爲1M
併發
使用上面的Math代碼,執行javap -v Math.class
查看描述信息:oracle
Classfile /E:/workspace/learn/blog-demo/target/classes/com/demo/jvm/Math.class
Last modified 2021-5-19; size 773 bytes
MD5 checksum 09a14d9236f6ceccf06184daa6cef7d9
Compiled from "Math.java"
public class com.demo.jvm.Math minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #8.#29 // java/lang/Object."<init>":()V
#2 = Fieldref #30.#31 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #32.#33 // java/io/PrintStream.println:(I)V
#4 = Class #34 // com/demo/jvm/Math
#5 = Methodref #4.#29 // com/demo/jvm/Math."<init>":()V
#6 = Methodref #4.#35 // com/demo/jvm/Math.compute:()V
#7 = Fieldref #4.#36 // com/demo/jvm/Math.math:Lcom/demo/jvm/Math;
#8 = Class #37 // java/lang/Object
#9 = Utf8 math
#10 = Utf8 Lcom/demo/jvm/Math;
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 LocalVariableTable
#16 = Utf8 this
#17 = Utf8 compute
#18 = Utf8 a
#19 = Utf8 I
#20 = Utf8 b
#21 = Utf8 c
#22 = Utf8 main
#23 = Utf8 ([Ljava/lang/String;)V
#24 = Utf8 args
#25 = Utf8 [Ljava/lang/String;
#26 = Utf8 <clinit>
#27 = Utf8 SourceFile
#28 = Utf8 Math.java
#29 = NameAndType #11:#12 // "<init>":()V
#30 = Class #38 // java/lang/System
#31 = NameAndType #39:#40 // out:Ljava/io/PrintStream;
#32 = Class #41 // java/io/PrintStream
#33 = NameAndType #42:#43 // println:(I)V
#34 = Utf8 com/demo/jvm/Math
#35 = NameAndType #17:#12 // compute:()V
#36 = NameAndType #9:#10 // math:Lcom/demo/jvm/Math;
#37 = Utf8 java/lang/Object
#38 = Utf8 java/lang/System
#39 = Utf8 out
#40 = Utf8 Ljava/io/PrintStream;
#41 = Utf8 java/io/PrintStream
#42 = Utf8 println
#43 = Utf8 (I)V
{
public com.demo.jvm.Math();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/demo/jvm/Math;
public void compute();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: bipush 10
2: istore_1
3: iconst_5
4: istore_2
5: iload_1
6: iload_2
7: iadd
8: bipush 100
10: imul
11: istore_3
12: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
15: iload_3
16: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
19: return
LineNumberTable:
line 8: 0
line 9: 3
line 10: 5
line 11: 12
line 12: 19
LocalVariableTable:
Start Length Slot Name Signature
0 20 0 this Lcom/demo/jvm/Math;
3 17 1 a I
5 15 2 b I
12 8 3 c I public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #4 // class com/demo/jvm/Math
3: dup
4: invokespecial #5 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #6 // Method compute:()V
12: return
LineNumberTable:
line 15: 0
line 16: 8
line 17: 12
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 args [Ljava/lang/String;
8 5 1 math Lcom/demo/jvm/Math;
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: new #4 // class com/demo/jvm/Math
3: dup
4: invokespecial #5 // Method "<init>":()V
7: putstatic #7 // Field math:Lcom/demo/jvm/Math;
10: return
LineNumberTable:
line 5: 0
}
SourceFile: "Math.java"
複製代碼
Constant pool
部分就是這個類的常量池,這些信息都是存放在方法區裏面的,能夠看到,每一個指令都有對應的符號引用,這就是類元信息;使用的是直接內存,默認大小爲21M,容量滿了會觸發FullGC,這部分的內存會進行動態調整,若是上次GC回收了大量的內存,則會自動調小,若是沒有回收大量內存則會調大,可是最大不會超過設置的最大空間;app
通常狀況下,對象在被建立出來是存放在eden區,當eden區放滿了以後會觸發一次MinorGC,使用複製算法把剩餘對象放到其中一個Survivor區,繼續進行對象的 建立->清理,當年輕代的那些被GC了15次尚未回收的對象放入老年代,老年代通常狀況下在沒法存放對象的時候會進行一次FullGC(根據具體的垃圾回收器決定,CMS會經過一個併發清理的參數設置何時執行FullGC),此次GC的範圍包含年輕代,老年代,元空間;若是在老年代沒有回收出來可用空間則會直接拋出OOM。
能夠跑下面這個demo,使用jvisualvm
工具查看對象的分配過程jvm
public class OOMTest {
private byte[] bytes = new byte[1024 * 1024];
public static void main(String[] args) {
ArrayList<OOMTest> list = new ArrayList<>();
for(;;) {
list.add(new OOMTest());
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
複製代碼
-XX:MetaspaceSize
元空間的默認大小
-XX:MaxMetaspaceSize
設置元空間的最大內存,若是不設置的話會一直擴容,直到直接內存溢出OOM,經驗值設置爲256M;
-Xss
每一個線程的棧大小,默認爲1M
-Xms
初始堆大小,默認物理內存的1/64
-Xmx
最大堆大小,默認物理內存的1/4
-Xmn
新生代大小
-XX:NewSize
設置新生代初始大小
-XX:NewRatio
默認2表示新生代佔年老代的1/2,佔整個堆內存的1/3。
-XX:SurvivorRatio
默認8表示一個survivor區佔用1/8的Eden內存,即1/10的新生代內存svg
路漫漫其修遠兮,吾將上下而求索