本文共 1904 字,大约阅读时间需要 6 分钟。
jvm启动流程
1、java或者javaw命令启动。
2、系统装载配置,根据当前路径和系统版本寻找jvm.cfg(jvm的配置文件)。
3、根据配置找到JVM.dll,jvm.dll为jvm的主要实现。
4、初始化JVM获得JNIEnv接口,JNIEnv为JVM接口,findClass等操作通过它实现。
5、找到main方法并运行。
jvm基本结构
一、PC寄存器
- 每个线程拥有一个PC寄存器。
- 在线程创建时创建
- 指向下一条指令的地址
- 执行本地方法时,PC的值为undefind。
二、方法区
- 保存装载的类信息 – 类型的常量池 – 字段,方法信息 – 方法字节码 (JDK6时,String等常量信息置于方法区;JDK7时,已经移动到了堆。)
- 通常和永久区(Perm)关联在一起,永久区保存相对于静止和相对稳定的数据。
三、Java堆
- 和程序开发密切相关
- 应用系统对象都保存在Java堆中
- 所有线程共享Java堆
- 对分代GC来说,堆也是分代的
- GC的主要工作区
四、Java栈(先进后出的数据结构)
- 线程私有的。
- 栈由一系列帧组成(因此Java栈也叫做帧栈)。
- 帧保存一个方法的局部变量、操作数栈、常量池指针。
- 每一次方法调用创建一个镇,并压栈。
- 局部变量表包含参数和局部变量,局部变量表中由很多槽位组成,每个槽位为32位。 示例:如果为static方法,第一个槽位是就是第一个参数。如果不是static方法,第一个槽位为这个对象本身this。
- 函数调用组成帧栈 示例:在下面这个递归方法调用时,每调用一次产生一个帧。
- 操作数栈 Java没有寄存器,所有参数传递使用操作数栈。 示例:a是局部变量0,b是局部变量1,c是局部变量2。压栈是将数据添加到操作数栈中去。
- 栈上分配 在一个方法中使用new创建对象,用完之后需要将对象delete掉,如果不这样做就很有可能发生内存溢出,如果在方法中只声明一个对象的引用,这样引用只会保存在栈中,随着方法的运行结束,自动清理。 分配的时候很大的时候,jvm会做一些优化。 (1)栈上分配一般只能分配小对象(一般几十个bytes),在没有逃逸的情况下,可以直接分配在栈上。(逃逸是指如果这个对象除了在这个线程要用,在其他线程也要用,就不可以分配在栈上。因为栈的线程是私有的。 ) (2)栈上分配–直接分配在栈上,可以自动回收,减轻GC的压力。 (3)大对象或者逃逸对象无法栈上分配。
五、栈、堆、方法去交互
内存模型
- 每一个线程有一个工作内存和主内存独立。
- 工作内存中存放主内存中变量值的拷贝。
1、当数据从主内存复制到工作内存存储时,必须出现两个动作:第一,由主内存执行的读(read)操作;第二,由工作内存执行相应的load操作。
2、当数据从工作内存拷贝到主内存时,也出现两个操作:第一个,由工作内存指定相应的存储(store)操作;第二,由主内存执行相应的写(write)操作。
3、每一个操作都是原子的,即执行期间不会被中断。
4、对于普通变量,一个线程中更新的值,不会马上反应在其他变量中。如果需要在其他线程中立即可见,需要使用volatile关键字。
5、
可见性:一个线程修改了变量,其他线程可以立即知道。 6、保证可见性的方法:
- volatile关键字
- synchronized(unlock之前,写变量值回主存)
- final(一旦初始化完成,其他线程就可见) 7、有序性
- 在本线程内,操作都是有序的。
- 在线程外(多个线程)观察,操作都是无序的。(指令重排或主内存同步延时) 8、指令重排
- 线程内串行语义 线程A先执行writer()方法,a=1和flag=true执行的顺序可能打乱,不管谁先执行,在本线程内看来都是有序的。在线程B看来,a=1和falg=true的执行顺序就是无序的。
9、指令重排的基本原则
- 程序顺序原则:一个线程内保证语义的串行性。
- volatile规则,volatile变量的写,先发生于读。
- 锁规则:解锁(unlock)必然发生在随后的加锁(lock)前
- 传递性:A先于B,B先于C,那么A必然先于C
- 线程的start方法先于它的每一个动作
- 线程的所有操作先于线程的终结(Thread.join())
- 线程的中断(interrupt())先于被中断线程的代码
- 对象的构造函数执行结束先于finalize()方法。
编译和解释运行的概念
1、解释执行
- 解释执行以解释方式运行字节码
- 解释执行的意思是:第一句执行一句 2、编译运行(JIT)
- 将字节码编译成机器码
- 直接执行机器码
- 运行时编译
- 编译后性能有数量级的提升
转载地址:http://pnfoi.baihongyu.com/