jvm主要分成3个系统,类加载器系统,运行时数据区,执行引擎
执行引擎是什么东东? 其实就是c++的库函数,运行到最后都是要调用c++里的东西,
首先由类加载器读取编译后的class字节码文件到直接内存中,之后再根据class content的内容解析成class对象 这里的类加载器有三种,系统类加载器,扩展类加载器,启动类加载器(最顶级的类加载器,用c,c++写的),首先自己写的类,会由系统类加载器加载,但是由于类加载器的双亲委派机制,会委托加载器的上一级加载器负责帮忙加载,如果上一级加载器无法完成加载,则由自身加载器负责加载在加载的过程又分为3步,验证,准备,解析!
符号引用:也就是虚拟地址,因为在jvm运行字节码前,类的成员都还没有分配内存,这时不可能用直接引用,而是用符号代表指向这个成员的引用地址,符号引用存储在常量池中,在加载器的解析阶段,因为已经为一些静态变量分配啦内存,就可以通过解析这个符号得到字段或方法的真正地址,但具体如何实现解析的暂不清楚;
之后是初始化阶段(intializing) 真正的为类的静态成员变量赋初值!
负责存储指令地址(红框圈住的),指令地址存储的有详细指令即关于要进行的操作,程序的运行就是jvm通过寄存器中的指令地址不断更改从而来进行不同的操作,每个线程都有独有的寄存器;
再细分为栈帧,一个方法对应一个栈帧,栈帧又能再分为4个部分,每当执行字节码到一个方法时,都会再开辟一个逻辑意义上的栈帧,说他是逻辑意义,因为jvm并没有栈帧这个真实的物理结构
局部变量表
容量在加载阶段就已确定,基础单位slot,也是一段数组,有索引,存储的是方法的形参及方法内定义的局部变量
操作数栈
类似于局部变量表,就字面意思,存放操作数的,有时在执行一些算法指令时,会把数先压到操作数栈中,运算后再压回局部变量中去
动态链接
存放指向方法区中常量池里所属方法的地址
返回地址
cpu运算时靠段寄存器的值加偏移地址得到指令地址进行运算,jvm也是靠寄存器得到下一个指令地址,也就是说在执行某一方法时,把当前寄存器的值(即调用此方法指令的地址)写入这个返回地址,这样方法执行完后便可以返回到调用此方法的地方,然后继续执行下一段指令
是jvm的一种规范,其作用是存储已经被jvm加载过的类信息,如普通常量,静态变量,方法的一些信息等,如下图
常量池也在方法区存储,可以看java反编译后的字节码文件中内存结构;如下
//代码部分
public class Jvm {
static int a1 = 18;
public static void main(String[] args) {
final int a = 4;
int i = 1;
int j = 2;
System.out.println(new Jvm().add(1, 2));
}
public int add(int i, int j) {
return i + j;
}
}
//常量池 存储字面量及符号引用及string指向的子串
//字面量 就是字面的意思,如程序中咱们定义的aij,就是一个utf8的字符串存在常量池里
//符号引用 用特殊字符表示某个字段或方法的地址,比如下方 a1的符号引用i,add方法的符号引用(II)I,等等
Constant pool:
#1 = Methodref #9.#33 // java/lang/Object."":()V
#2 = String #34 // abcdefg
#3 = Fieldref #35.#36 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Class #37 // poyimima/algorithm/Jvm
#5 = Methodref #4.#33 // poyimima/algorithm/Jvm."":()V
#6 = Methodref #4.#38 // poyimima/algorithm/Jvm.add:(II)I
#7 = Methodref #39.#40 // java/io/PrintStream.println:(I)V
#8 = Fieldref #4.#41 // poyimima/algorithm/Jvm.a1:I
#9 = Class #42 // java/lang/Object
#10 = Utf8 a1
#11 = Utf8 I
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 LocalVariableTable
#17 = Utf8 this
#18 = Utf8 Lpoyimima/algorithm/Jvm;
#19 = Utf8 main
#20 = Utf8 ([Ljava/lang/String;)V
#21 = Utf8 args
#22 = Utf8 [Ljava/lang/String;
#23 = Utf8 s
#24 = Utf8 Ljava/lang/String;
#25 = Utf8 a
#26 = Utf8 i
#27 = Utf8 j
#28 = Utf8 add
#29 = Utf8 (II)I
#30 = Utf8 <clinit>
#31 = Utf8 SourceFile
#32 = Utf8 Jvm.java
#33 = NameAndType #12:#13 // "":()V
#34 = Utf8 abcdefg
#35 = Class #43 // java/lang/System
#36 = NameAndType #44:#45 // out:Ljava/io/PrintStream;
#37 = Utf8 poyimima/algorithm/Jvm
#38 = NameAndType #28:#29 // add:(II)I
#39 = Class #46 // java/io/PrintStream
#40 = NameAndType #47:#48 // println:(I)V
#41 = NameAndType #10:#11 // a1:I
#42 = Utf8 java/lang/Object
#43 = Utf8 java/lang/System
#44 = Utf8 out
#45 = Utf8 Ljava/io/PrintStream;
#46 = Utf8 java/io/PrintStream
#47 = Utf8 println
#48 = Utf8 (I)V
按照区域分为三部分,新生代,老年代,及方法区(又叫非堆,特意与堆分开,其本质还是堆的一部分)(1.7以前的实现是永久代,1.8后的实现是元空间),新生代占据1/3的空间,老年代占据2/3的空间,new 出的对象都存放在堆中