JVM内存模型总结

JVM内存模型

整体架构

JVM = 类加载器(classloader) + 执行引擎
(executionengine) + 运行时数据区域(runtime dataarea)
java每个线程都有一个虚拟机栈

类加载器 ClassLoader

1.加载过程 7 个步骤 加载–>验证–>准备–>解析–>初始化–>使用–>卸载
在这里插入图片描述
2.ClassLoader–>BaseDexClassLoader–>PathClassLoder ,DexClassLoader

3.双亲委托模式 先走父类的加载器加载类,若果没有找打父类才会轮到自己,好处 避免重复加载 安全

运行时数据区

  • 运行时数据区 包涵 5 大部分
    方法区、堆、栈、本地方法栈、程序计数器

1.程序计数器 :

我们知道对于一个处理器(如果是多核cpu那就是一核),在一个
确定的时刻都只会执行一条线程中的指令,一条线程中有多个指
令,为了线程切换可以恢复到正确执行位置,每个线程都需有独
立的一个程序计数器,不同线程之间的程序计数器互不影响,独立存储。    

2.本地方法栈 : 简单的理解为 C 代码的执行去

3.堆 : 简单的说就是对象的存储区,它是被所有线程共享的一块区域

堆是java虚拟机管理内存最大的一块内存区域,因为堆存放的
对象是线程共享的,所以多线程的时候也需要同步机制。
堆 回收算法使用的复制算法 效率高 没有碎片 利用率低
分为三个区 eden  from  to (survivor) 按照 8:1:1因为大多数的对象都是朝生夕死的。

4.栈 :

栈描述的是Java方法执行的内存模型。
每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,
操作栈,动态链接,方法出口等信息。每一个方法被调用的过程
就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。

5.方法区

方法区同堆一样,是所有线程共享的内存区域,为了区分堆,又
被称为非堆。用于存储已被虚拟机加载的类信息、常量、静态变
量,如static修饰的变量加载类的时候就被加载到方法区中。

6.GC

  1. 堆的回收为了高效的回收,jvm将堆分为三个区域
    1.新生代(Young Generation)NewSize和MaxNewSize分别可以控制年轻代的初始大小和最大的大小
    2.老年代(Old Generation)
    3.永久代(Permanent Generation)【1.8以后采用元空间,就不在堆中了】

  2. 对象是否存活

    1. 引用计数算法
      早期判断对象是否存活大多都是以这种算法,这种算法判断很简单,简单来说就是给对象添加一个引用计数器,每当对象被引用一次就加1,引用失效时就减1。当为0的时候就判断对象不会再被引用。
      优点:实现简单效率高,被广泛使用与如python何游戏脚本语言上。
      缺点:难以解决循环引用的问题,就是假如两个对象互相引用已经不会再被其它其它引用,导致一直不会为0就无法进行回收。
    2. 可达性分析算法
      目前主流的商用语言[如java、c#]采用的是可达性分析算法判断对象是否存活。这个算法有效解决了循环利用的弊端。
      它的基本思路是通过一个称为“GC Roots”的对象为起始点,搜
      索所经过的路径称为引用链,当一个对象到GC Roots没有任何引用跟它连接则证明对象是不可用的。
  3. 回收算法

1.标记/清除算法【最基础】 (老年代 标记清楚 、整理 新生代 是复制算法)

标记 也是红灰白  对灰色进行清除 标记整理是红色进行整
理 ,清楚会有很多碎片,效率高

2.复制算法

复制内存区域,标记 红蓝灰白色   红色不可回收  灰色可回
收  白色没有分配 蓝色 预留 , 效率高,内存复制没有碎
片,缺点  利用率只有一半() 

3.标记/整理算法

jvm采用`分代收集算法`对不同区域采用不同的回收算法。
其中新生代使用的是复制算法,老年代使用的是标记清除、标记整理算法。·

jvm 三大特性 可见性 原子性 有序性

volatile 具有可见性和禁止命令重排序,不保证原子性。所以能够达到一次修改其他线程可见

原子性

即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

  • 例如 i=0 只有一步操作 把0赋给变量i i++则不是,先取出i 再执行i+1 最后把结果赋给i 进行了三部,不符合原子性
  • 在单线程环境下我们可以认为以上都是原子性操作,但是在多线程环境下则不同,Java只保证了基本数据类型的变量和赋值操作才是原子性的(注:在32位的JDK环境下,对64位数据的读取不是原子性操作*,如long、double)。要想在多线程环境下保证原子性,则可以通过锁、synchronized来确保。

可见性

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

  • 在多线程环境下,一个线程对共享变量的操作对其他线程是不可见的。

有序性

有序性:即程序执行的顺序按照代码的先后顺序执行。

  • 在Java内存模型中,为了效率是允许编译器和处理器对指令进行重排序,当然重排序它不会影响单线程的运行结果,但是对多线程会有影响。

volatile实现原理

volatile可以保证线程可见性且提供了一定的有序性,但是无法保证原子性,即 volatile 保证了 可见性和有序性 没有原子性 。在JVM底层volatile是采用“内存屏障”来实现的。

  • 问题的提出 计算机在运行程序时,每条指令都是在CPU中执行的,在执行过程中势必会涉及到数据的读写。我们知道程序运行的数据是存储在主存中,这时就会有一个问题,读写主存中的数据没有CPU中执行指令的速度快,如果任何的交互都需要与主存打交道则会大大影响效率,所以就有了CPU高速缓存。CPU高速缓存为某个CPU独有,只与在该CPU运行的线程有关。

有了CPU高速缓存虽然解决了效率问题,但是它会带来一个新的问题:数据一致性。在程序运行中,会将运行所需要的数据复制一份到CPU高速缓存中,在进行运算时CPU不再也主存打交道,而是直接从高速缓存中读写数据,只有当运行结束后才会将数据刷新到主存中。举一个简单的例子:

    i++i++
    当线程运行这段代码时,
    首先会从主存中读取i( i = 1),
    然后复制一份到CPU高速缓存中,
    然后CPU执行 + 1 (2)的操作,
    然后将数据(2)写入到告诉缓存中,
    最后刷新到主存中。
    其实这样做在单线程中是没有问题的,有问题的是在多线程中。如下:
    
    假如有两个线程A、B都执行这个操作(i++),按照我们正常的逻辑思维主存中的i值应该=3,但事实是这样么?分析如下:
    
    两个线程从主存中读取i的值(1)到各自的高速缓存中,然后线程A执行+1操作并将结果写入高速缓存中,最后写入主存中,此时主存i==2,线程B做同样的操作,主存中的i仍然=2。所以最终结果为2并不是3。这种现象就是缓存一致性问题。
  • 解决缓存一致性方案有两种:
  1. 通过在总线加LOCK#锁的方式
    通过缓存一致性协议
    但是方案1存在一个问题,它是采用一种独占的方式来实现的,即总线加LOCK#锁的话,只能有一个CPU能够运行,其他CPU都得阻塞,效率较为低下。

  2. 第二种方案,缓存一致性协议(MESI协议)它确保每个缓存中使用的共享变量的副本是一致的。其核心思想如下:当某个CPU在写数据时,如果发现操作的变量是共享变量,则会通知其他CPU告知该变量的缓存行是无效的,因此其他CPU在读取该变量时,发现其无效会重新从主存中加载数据。

一个int变量,用volatile修饰,多线程去操作++,线程安全吗?

    不安全。volatile只能保证可见性,并不能保证原子性。
    i++实际上会被分成多步完成:
    1)获取i的值;
    2)执行i+1;
    3)将结果赋值给i。
    volatile只能保证这3步不被重排序,多线程情况下,可能两个线程同时获取i,执行i+1,然后都赋值结果2,实际上应该进行两次+1操作。

那如何才能保证i++线程安全?

可以使用java.util.concurrent.atomic包下的原子类,如AtomicInteger。
其实现原理是采用CAS自旋操作更新值。CAS即compare and swap的缩写,中文翻译成比较并交换。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。自旋就是不断尝试CAS操作直到成功为止。

CAS实现原子操作会出现什么问题?

ABA问题。因为CAS需要在操作之的时候,检查值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成,有变成A,那么使用CAS进行检查时会发现它的值没有发生变化,但实际上发生了变化。ABA问题可以通过添加版本号来解决。Java 1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。
循环时间长开销大。pause指令优化。
只能保证一个共享变量的原子操作。可以合并成一个对象进行CAS操作。

  • 总结 :volatile 可见性的原理是通过内存屏障来实现的。对这个标志符修饰的变量不再从高速缓冲中读取数据,让所有访问该变量的线程都直接从主存中读取数据,以此来保证数据的可见性。

参考 https://www.jianshu.com/p/76959115d486 //这个文章比较好,讲解了volatile 同时还讲解了jvm内存模型
https://www.cnblogs.com/chenssy/p/6379280.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值