为什么要学习
我们为什么要学习jvm?給大家一个代码列子,说明一下
public class CatTest {
public CatTest() {
Object t=new Object();
}
}
接下来执行javac CatTest.java 后面调用javap -v CatTest.class
这里可以看出Object t=new Object(),这代码执行了好多个jvm指令码,但是学过操作系统的肯定知道,cpu执行指令有可能是乱序的,是不是我们的代码就乱了?单线程结果一般都是正确的,但是多线程情况下有可能出问题,但是Java也给我们提供了一些工具,去保证在多线程数据的安全性。
硬件层基础
存储器的层次结构
如何保证数据一致性
- 总线锁: 使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存。
- MESI Cache缓存一致性协议
- 每个cache line标记四种状态(invaild、exclusive、modified、shard)
- 大家可以参考一下别的文章,会理解的更透彻点。
缓存行
- Cache 中的数据是按块读取的,当CPU访问某个数据时,会假设该数据附近的数据以后会被访问到,因此,第一次访问这一块区域时,会将该数据连同附近区域的数据(共64字节)一起读取进缓存中,那么这一块数据称为一个Cache Line 缓存行。
- 缓存系统是以缓存行为单位存储的。目前主流的CPU Cache的Cache Line大小都是64字节。
不是所有数据都会被缓存,比如一些较大的数据,缓存行无法容下,那么就只能每次都去主内存中读取。
缓存行对齐:伪对齐
缓存行的效率问题?
- 如果你的X和y在同一块缓存行中,且两个字段都用volatile修饰了那么将来两个线程再修改的时候,就需要将x和y发生修改的消息告诉另外一个线程,让它重新加载对应缓存,然而另外一个线程并没有使用该缓存行中对应的内容,只是因为缓存行读取的时候跟变量相邻,也会需要重新读取。这就会产生效率问题。
- 解决起来也简单,我们将数据中的两个volatile之间插入一些无用的内存,将第二个值挤出当前缓存行,那么执行的时候,就不会出现相应问题了。提高代码效率。
执行顺序
现代cpu执行指令,都会打乱之前的执行顺序去执行。前提是2条指令没有依赖关系。
如何保证特定情况下有序执行:
cpu内存屏障
Intel设计得比较简单:
①sfence:也就是save fence,写屏障指令。在sfence指令前的写操作必须在sfence指令后的写操作前完成。
②lfence:也就是load fence,读屏障指令。在lfence指令前的读操作必须在lfence指令后的读操作前完成。
③mfence:在mfence指令前得读写操作必须在mfence指令后的读写操作前完成。