JVM——内存模型

1.java内存模型

java内存模型是Java Memory Model的意思。简单的说,JMM定义了一套在多线程读写共享数据(成员变量、数组)时,对数据的可见性、有序性和原子性的规则和保障。

1.1 原子性

Java 内存模型保证了 read、load、use、assign、store、write、lock 和 unlock 操作具有原子性,例如对一个 int 类型的变量执行 assign 赋值操作,这个操作就是原子性的。

1.2实例

synchronized(同步关键字)
语法:

synchronized(对象){
 要作为操作代码
}

用synchronized解决并发问题:
在这里插入图片描述
主要:上例中t1和t2线程必须用synchronized锁住同一个obj对象,如果t1锁住的是m1对象,t2锁住的是m2对象。就好比两个人分别进入两个不同的房间,没法起到同步的效果。

2.可见性

2.1退不出的循环

在这里插入图片描述

2.2解决方法

volatile(易变关键字)
它可以用来修饰成员变量和静态变量,他可以避免线程从自己的工作缓冲中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操主存。
在这里插入图片描述

2.3 可见性

所谓可见性,就是指一个线程改变了共享变量之后,其他线程能够立即知道这个变量被修改。我们知道在Java内存模型中,不同线程拥有自己的本地内存,而本地内存是主内存的副本。如果线程修改了本地内存而没有去更新主内存,那么就无法保证可见性。
前面例子提案的实际就是可见性,它保证的是在多个线程之间,一个线程对volatile变量的修改对另一个线程可见,不能保证原子性,仅用在一个写线程,多个读线程的情况。
注意
synchronized语句块既可以保证代码块的原子性,也同时保证代码块内变量的可见性。但缺点是synchronized是属于重量级操作,性能相对更低

如果在前面实例的死循环中加入System.out.println()会发现即使不加volatile修饰符,线程t也能正确看到对run变量的修改了,为什么?
在这里插入图片描述
通过看System.out.println();的源码不难发现:

    public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

println方法内部实际上也是加了synchronized关键字的,因此可以保证可见性和原子性

3.有序性

3.1有序性理解

同一个线程内,JVM会在不影响正确性的前提下,可以调整语句的执行顺序。

3.2怎么实现有序性

(1)volatile关键字通过添加内存屏障的方式来禁止指令重排,即重排序时不能把后面的指令放到内存屏障之前。
(2)也可以通过 synchronized 来保证有序性,它保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码。

happens-before

happens-before规定了那些写操作对其他线程的读操作可见,它是可见性与有序性的一套规则总结:
(1)线程解锁m之前对变量的写,对于接下来对m加锁的其它线程对该变量大的读可见。
在这里插入图片描述
(2)线程对volatile变量的写,对接下来其它线程对该变量的读可见
在这里插入图片描述
(3)线程结束前对变量的写,对其它线程得知它结束后的读可见(比如其他线程调用t1,isAlive()或t1,join()等待它结束)
在这里插入图片描述
(4)线程t1打断t2(interrupt)前对变量的写,对于其他线程得知t2被打断后对变量的读可见(通过t2.interrupted或t2.isInterrupt)
在这里插入图片描述
(5)对变量默认值(0,false,null)的写,对其他线程对该变量的读可见
(6)具有传递性,如果x hb->并且y hb->z那么x hb->z
变量都是指成员或静态成员变量

CAS与原子类

CAS

CAS即Compare and Swap,它体现的一种乐观锁的思想,比如多个线程要对一个共享的整形变量执行+1操作:
在这里插入图片描述
获取共享变量时,为了保证该变量的可见性,需要使用volatile修饰。结合CAS和volatile可以实现无锁并发,使用于竞争不激烈、多核CPU的场景下。
(1)因为没有使用synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一
(2)但如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响
CAS底层依赖于一个Unsafe类来直接调用操作系统底层的CAS指令

4.2乐观锁与悲观锁

  • CAS是基于乐观锁的思想:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系,我吃亏点再重试呗。
  • synchronized是基于悲观锁的思想:最悲观的估计,得防着其他线程来修改共享变量,我上了锁你们都别想改,我们改完了解开锁,你们才有机会。

4.3原子操作类

juc(java.util.concurrent)中提供了原子操作类,可以提供线程安全的操作,例如:AtomicInteger、AtomicBoolean等,它们底层就是采用CAS技术+volatile来实现的。
可以使用AtomicInteger改写之前的例子:
在这里插入图片描述

5synchronized优化

Java HotSpot虚拟机中,每个对象都有对象头(包括class指针和Mark)。Mark Word平时存储这个对象的哈希码、分代年龄,当加锁时,这些信息就根据情况被替换为标记位、线程锁记录指针、重量级锁指针、线程ID等内容。

5.1轻量级锁

如果一个对象虽然有多线成访问,但多线程访问的时间时错开的(也就是没有竞争),那么可以使用轻量级锁来优化。

5.2锁膨胀

如果在尝试加轻量级锁的过程,CAS操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。

重量级锁

重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自选成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。

在Java6之后自选锁时自适应的,比如对象刚刚的一次自选操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自选甚至不自旋,总之,比较智能。

  • 自选会占用CPU时间,单核CPU自旋就是浪费,多核CPU自旋才能发挥优势。
  • 好比等红绿灯时汽车是不是熄火,不熄火相当于自旋(等待时间短了划算),熄火了相当于阻塞
  • java7之后不能控制是否开启自旋功能

偏向锁

轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行CAS操作。Java6中引入了偏向锁来做进一步优化:只有第一次使用CAS将线程ID设置到对象的Mark Word头,之后发现这个线程ID是自己的就表示没有竞争,不用重新CAS。

  • 撤销偏向需要将持锁线程升级为轻量级锁,这个过程中所有线程需要暂停。
  • 访问对象的hashCode也会撤销偏向锁
  • 如果对象被多个线程访问,但没有竞争,这是偏向了线程T1的对象仍有机会重新偏向T2,重偏向会重置对象的Thread ID
  • 撤销偏向到达某个阈值,整个类的所有对象都会变为不可偏向的
  • 可以主动使用-XX:-UseBiasedLocking禁用偏向锁

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
JVMJava虚拟机)的分代模型是一种内存管理策略,将堆内存划分为不同的代(Generation),包括年轻代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation,JDK8之后被元空间(Metaspace)取代)。下面是JVM分代模型的执行流程: 1. 初始阶段:当Java应用程序启动时,JVM会为其分配一块初始的堆内存空间。此时,年轻代和老年代都是空的。 2. 对象创建:当Java程序创建对象时,对象会被分配在年轻代的Eden区域。如果Eden区域没有足够的空间来存放新创建的对象,就会触发一次垃圾回收(Minor GC)。 3. Minor GC:在Minor GC中,垃圾回收器会扫描年轻代的Eden区域和Survivor区域,将不再被引用的对象进行回收。存活的对象会被移动到Survivor区域中的一个空闲区域。 4. 对象晋升:当一个对象经过多次Minor GC后仍然存活,它会被晋升到老年代。晋升条件可以根据不同的垃圾回收器而有所不同。 5. Major GC:当老年代空间不足时,会触发一次Major GC(也称为Full GC)。在Major GC中,垃圾回收器会扫描整个堆内存,对不再被引用的对象进行回收。 6. 永久代/元空间:永久代(JDK8之前)或元空间(JDK8及以后)用于存放类的元数据和常量池等信息。当类的元数据不再被使用时,会触发一次永久代/元空间的垃圾回收。 7. 内存分配担保:在进行垃圾回收时,如果老年代的空间不足以存放新创建的对象,JVM会进行一次内存分配担保。即使触发了垃圾回收,也能保证新创建的对象能够顺利分配到老年代。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

One玛玛小王子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值