jvm——内存模型

1.java内存模型

1.1 原子性

1.2 问题分析

这里与局部变量自增不同,局部变量调用iinc是在局部变量表槽位上进行自增。

静态变量是在操作数栈自增。

这里的主内存和工作内存时再JMM里的说法。

因为操作系统是时间片切换的多个线程轮流使用CPU.

1.3解决方法

JMM中通过synchronized(同步关键字)保证原子性。

使用synchronized减i++和i--的分别的所有字节码指令作为一个整体运行。

使用synchronized加锁的力度最好大一点,只锁个i++就只有四条指令,不然增加时间。

2.可见性

2.1退不出的循环

运行发现停不下来了。

运行超过一秒之后就触发C2编译器进行优化了。run被读到了线程的局部变量表里面。

1s后再修改也看不见了。

2.2解决方法

2.3可见性

volatile只适用于一个写线程和多个读线程的情况。

println底层有synchronized关键字,也可以强制线程去到主存里面取值。

synchronized可以保证可见性和原子性。

3.有序性

3.1诡异的结果

应该是指令顺序为了优化发生了改变导致ready=true时num还没获取到2。

3.2解决方法

@Outcome注解就是检查感兴趣的结果。

1或4就划分为可接受的,ok表示之中

0划分到另一个。

清除并重新编译

生成一个源码jar包和一个压测入口jar包。

运行测试包进行压测。

结果中有两种,一个是带了关闭了分层编译,还有一个是没带任何参数。

两种情况都有出现0的结果。

说明指令重排问题确实有。

解决方法就是使用volatie关键字。

再次压测就不会有指令重排的问题了。

3.3有序性理解

双重检测法创建单例 就需要volatile防止指令重排

如果在创建的代码上加锁力度就太大了,创建该对象了,后续get该对象是不需要加锁的。

所以有了上面的双重判断,先判断是否实例为空,为空就加锁,加完锁再判断实例为空,任然为空就创建。

第一个if是为了提高效率,实例创建后,就不用一直获得锁对象。

第二个if是防止别的线程创建另一个实例。

0分配空间,3复制多一个引用进操作数栈4一个引用去调用构造方法7另一个引用交给了静态变量

t2直接返回了拿到了一个不完整的实例。

3.4 happens-before

就相当于打个标记,标记前改了值, 标记后看得见

4.CAS与原子类

4.1 CAS

CAS 是 Compare And Swap( 比较并替换**)**的缩写,当值为预期值的时候,就将该值替换为预期的值。

CAS 也是实现原子操作的一种方法。

4.2乐观锁与悲观锁

4.3原子操作类

测试结果为0。

5.synchronized 优化(这个部分要先去看JUC)

5.1轻量级锁

A有两次加锁,一次轻量锁,一次重量锁。升级过程就是锁膨胀。

轻量级锁的加锁过程。

线程和对象之间交换定情信物,对象给出了Mark World存在线程的锁记录里面,线程给出了锁记录地址。

Mark Word只有八个字节,解锁时才会将对象的Mark Word恢复。

锁了A之后去访问B尝试锁B结果发现已经锁了,但是是自己上的锁所以还是可以访问B.

然后都访问完了之后就A和B都解锁。

解锁过程是把MarkWorld都还回去然后取出对象上的锁标记。

5.2锁膨胀

升级为重量级锁会把标记从01变成10,并在对象头里面加入重量级锁的指针,该指针用于线程1在解锁时唤醒阻塞中的线程。

5.3重量锁_自旋

这里线程2不会立刻 阻塞,阻塞需要把当前状态保存下来。

直接采用了自旋优化,先不停,不停重试,在阈值之内等到了对象解锁

自旋失败 就进入阻塞状态了。

5.4 偏向锁

锁重入就是要锁不同的代码块时对同一个对象加锁。

5.5其它优化

上锁时间过长可能会导致轻量锁变成重量锁。

相关推荐
吴冰_hogan6 小时前
JVM(Java虚拟机)的组成部分详解
java·开发语言·jvm
东阳马生架构13 小时前
JVM实战—1.Java代码的运行原理
jvm
ThisIsClark16 小时前
【后端面试总结】深入解析进程和线程的区别
java·jvm·面试
王佑辉16 小时前
【jvm】内存泄漏与内存溢出的区别
jvm
大G哥18 小时前
深入理解.NET内存回收机制
jvm·.net
泰勒今天不想展开18 小时前
jvm接入prometheus监控
jvm·windows·prometheus
东阳马生架构2 天前
JVM简介—3.JVM的执行子系统
jvm
程序员志哥2 天前
JVM系列(十三) -常用调优工具介绍
jvm
后台技术汇2 天前
JavaAgent技术应用和原理:JVM持久化监控
jvm