一.Java内存区域
1.方法区:用于存储已被JVM加载的类信息,常量,静态变量。运行时常量池就在方法区中。
2.JVM堆:主要用于存储实例对象。
3.程序计数器:主要代表当前线程所执行的字节码行号指示器。
4.虚拟机栈:代表Java方法执行的内存模型,每个方法执行都会创建栈帧存储方法的变量表,操作数栈,运行链接方法,返回 值等。
5.本地方法栈:与Native相关,一般不需要关心。
二.Java内存模型
JVM运行的实体是线程,每个线程分配有一个工作线程,存储私有数据,JMM规定所有变量存储在主内存,这些数据是线程间共享的,但线程对数据的操作必须在工作内存中进行,首先从主内存拷贝到自己的工作内存,操作这个副本,完成后再写入主内存。
1.原子性:指一个操作是不可中断的,即使在多线程下,不被其他Thread影响。
2.有序性:对于单线程,代码可以看做串行执行所以结果总是一致,但多线程由于指令重排序代码执行未必有序。
3.可见性:某一个线程修改了某个共享变量的值,其他线程能够马上感知。
三.指令重排序
1.编译器重排
编译器在不改变单线程语义的前提下,重新安排语句执行顺序,但在多线程情形下,程序不会串行执行,最终语义不明,程序混乱。
2.处理器指令重排
现代处理器采用指令并行将多条指令重叠执行,若不存在依赖性可以调整执行顺序。
从指令执行的角度来说一条指令可以分为多个步骤完成:
取值IF——>译码和取寄存器操作数ID——>执行或者有效地址计算EX——>存储器访问MEM——>写回WB
为了提高硬件利用率,CPU指令是按流水线技术执行的,假设每个步骤的耗时1秒,当指令1还未完成时,第2条指令若等待其全部完成需要5秒,若指令2利用空闲的硬件开始执行,只需要等待1秒。
流水线技术会产生停顿,即中断,假如指令3的操作需要1和2的存储结果,但指令1,2还未执行到MEM,所以产生中断。中断会影响程序的执行效率。处理器指令重排序将计算结果没有依赖性的指令移动,这样可以减少中断的发生。
四.JMM的happens—before原则
1.串行执行原则:单线程代码顺序执行。
2.锁规则:对于同一把锁,加锁解锁动作必须又先后,不可重叠。
3.volatile规则:volatile变量每次被线程访问时,都强迫从主内存中读该变量的值,操作完成立刻写回主内存。
4.线程启动规则:线程A在线程B执行Start方法前修改的共享变量对线程B可见。
5.传递性:线程A先于B,B先于C,则A必先于C。
6.线程中断规则:在线程中断事件发生前调用Thread.interrupt可以检测线程是否中断。
7.线程终止规则:线程B在终止之前修改的共享变量终止后对A可见。
8.对象终止规则:对象构造函数的执行,结束先于finalize()。
五.volatile的内存语义
JVM提供的轻量级同步机制。
1.保证修饰的变量对所有线程可见,新值可以被其他线程立即得知。
2.禁止指令重排序。