一、认识jvm
JVM是Java Virtual Machine的简称。意为Java虚拟机
虚拟机是指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统
例如:VMWare、Visual Box、JVM
VMWare或者Visual Box都是使用软件模拟物理CPU的指令集
JVM使用软件模拟Java 字节码的指令集
JVM的编译:
javap -c的指令是将一段二进制代码转换为汇编的格式。
二、JVM运行机制
JVM启动流程
JVM基本结构
类加载器系统:classloader将class文件加载到jvm的内存空间中
本地方法栈:native标识的方法
pc寄存器:每个线程拥有一个pc寄存器,在线程创建时创建,指向下一条指令的地址。
方法区:主要保存的就是类的相关信息,类型的常量池,字段、方法信息,方法字节码。通常和永久区关联在一起。
java堆:
- 1、和程序开发密切相关
- 2、应用系统对象都保存在Java堆中
- 3、所有线程共享Java堆
- 4、对分代GC来说,堆也是分代的
- 5、GC的主要工作区间
java栈:
- 1、线程私有
- 2、栈由一系列帧组成(因此Java栈也叫做帧栈)
- 3、帧保存一个方法的局部变量、操作数栈、常量池指针
- 4、每一次方法调用创建一个帧,并压栈
图说明:上方右边显示了100+98的过程,数据利用操作数栈临时保存。
Java栈 – 栈上分配
1、小对象(一般几十个bytes),在没有逃逸的情况下(栈是线程私有的,如果对于全局的变量就不能分配在栈上),可以直接分配在栈上
2、直接分配在栈上,可以自动回收(不需要GC清理),减轻GC压力
3、大对象或者逃逸对象无法栈上分配。
jvm内存模型
每一个线程都有一个工作内存和主存独立
工作内存存放主存中变量的值的拷贝。
1、当数据从主内存复制到工作存储时,必须出现两个动作:第一,由主内存执行的读(read)操作;第二,由工作内存执行的相应的load操作;当数据从工作内存拷贝到主内存时,也出现两个操作:第一个,由工作内存执行的存储(store)操作;第二,由主内存执行的相应的写(write)操作
2、每一个操作都是原子的,即执行期间不会被中断
3、对于普通变量,一个线程中更新的值,不能马上反应在其他变量中,如果需要在其他线程中立即可见,需要使用 volatile 关键字
volatile的说明:
public class Test extends Thread {
private volatile boolean stop = true;
public void stopme() {
stop = false;
}
@Override
public void run() {
int i = 0;
while (stop) {
i++;
}
System.out.println("停止" + i);
}
public static void main(String[] args) throws InterruptedException {
Test t = new Test();
t.start();
Thread.sleep(1000);
t.stopme();
}
}
上述代码的运行结果是:线程会结束。如果stop变量不使用volatile声明,则不会结束,因为线程永远适用的是拷贝到自己工作空间的值,而这个stop标识一直为true。
可见性:
一个线程修改了变量,其他线程可以立即知道
保证可见性的方法:
- volatile
- synchronized (unlock之前,写变量值回主存)
- final(一旦初始化完成,其他线程就可见)
有序性:
在本线程操作内,操作都是有序的;(从结果看保证的结果是一致的)
而在线程外,操作都是无序的。产生这个的原因是指令重排或者主内存同步延时。
指令重排的基本原则
- 1、程序顺序原则:一个线程内保证语义的串行性
- 2、volatile规则:volatile变量的写,先发生于读
- 3、锁规则:解锁(unlock)必然发生在随后的加锁(lock)前
- 4、传递性:A先于B,B先于C 那么A必然先于C
- 5、线程的start方法先于它的每一个动作
- 6、线程的所有操作先于线程的终结(Thread.join())
- 7、线程的中断(interrupt())先于被中断线程的代码
- 8、对象的构造函数执行结束先于finalize()方法
字节码运行的两种方式:
解释运行
- 解释执行以解释方式运行字节码
- 解释执行的意思是:读一句执行一句
编译运行(JIT)
- 将字节码编译成机器码
- 直接执行机器码
- 运行时编译(也就是运行前先将字节码编译成机器码)
- 编译后性能有数量级的提升