jvm分为以下几部分:
1、程序计数器
主要用来获取下一条要执行的指令,比如线程切换时候,恢复上次线程执行到的位置。因此属于线程私有的
2、方法栈
- 线程执行方法时,临时创建,属于线程私有,生命周期与线程生命周期一致
- 方法的执行就是一个栈帧的入栈出栈过程
- 栈帧
- 局部变量表(slot)
- 下标为0的slot保存着this指针
- boolean、byte、char、long、short、int、float、double、reference、returnAddress
-操作数栈
代表着方法的执行
3、堆
- 用来分配对象
- 分为YG与OG两部分
- YG分为Eden+Survivor1+Survivor2三部分,在进行jvm优化的时候,应该注意:YG应该小于OG,因为如果YG>OG,频繁的monitor gc,可能导致对象被复制到OG中,最终导致频繁Full GC。同时S1、S2也不应过小,因为在monitor gc时,S1或者S2放不下的对象,会被复制到OG中,可能导致FULL GC。
- YG+OG<物理机内存,因为如果大于的话,会导致一部分对象swap到硬盘进行持久化,这样会导致在GC时候,需要将硬盘数据加载到内存,导致性能下降
4、方法区
- Classloader 引用
- 运行时常量池
数值型常量
字段引用
方法引用
属性
- 字段数据
针对每个字段的信息
字段名
类型
修饰符
属性(Attribute)
- 方法数据
每个方法方法名
每个方法返回值类型
每个方法参数类型(按顺序)
每个方法修饰符
每个方法属性
- 方法代码
每个方法字节码
每个方法操作数栈大小
每个方法局部变量大小
每个方法局部变量表
每个方法异常表
每个方法每个异常处理器
每个方法开始点
每个方法结束点
每个方法异常处理代码的程序计数器(PC)偏移量
每个方法被捕获的异常类对应的常量池下标
5、本地方法栈
主要用来调用native方法,当调用native方法时,程序计数器设置为undefined
方法的执行过程:
package org.jvminternals;
public class SimpleClass {
public void sayHello() {
System.out.println(“Hello”);
}
}
编译成class文件:
public class org.jvminternals.SimpleClass
SourceFile: “SimpleClass.java”
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#17 // java/lang/Object.”<init>”:()V
#2 = Fieldref #18.#19 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #20 // “Hello”
#4 = Methodref #21.#22 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #23 // org/jvminternals/SimpleClass
#6 = Class #24 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lorg/jvminternals/SimpleClass;
#14 = Utf8 sayHello
#15 = Utf8 SourceFile
#16 = Utf8 SimpleClass.java
#17 = NameAndType #7:#8 // “<init>”:()V
#18 = Class #25 // java/lang/System
#19 = NameAndType #26:#27 // out:Ljava/io/PrintStream;
#20 = Utf8 Hello
#21 = Class #28 // java/io/PrintStream
#22 = NameAndType #29:#30 // println:(Ljava/lang/String;)V
#23 = Utf8 org/jvminternals/SimpleClass
#24 = Utf8 java/lang/Object
#25 = Utf8 java/lang/System
#26 = Utf8 out
#27 = Utf8 Ljava/io/PrintStream;
#28 = Utf8 java/io/PrintStream
#29 = Utf8 println
#30 = Utf8 (Ljava/lang/String;)V
{
public org.jvminternals.SimpleClass();
Signature: ()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 Lorg/jvminternals/SimpleClass;
public void sayHello();
Signature: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String “Hello”
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 6: 0
line 7: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lorg/jvminternals/SimpleClass;
}
方法的调用,首先从方法区的常量池中获取变量地址,然后在从相应地址获取值。例如:
public Class Father{
public static void main(String[]args){
Father father=new Father();
father.sayHello();
}
public void sayHello(){
System.out.println(“hello”)
}
}
1、调用new指令,生成对象,同时将对象的引用father压入操作数栈顶部
2、调用invokevirtual从方法区常量池中获取sayHello所在地址
3、执行sayHello()方法