JVM学习笔记整理



1.JVM是什么

Java平台可分为两部分,即Java虚拟机(Java virtual machine,JVM)和Java API类库
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
Java虚拟机主要分为五大模块:类装载器子系统、运行时数据区、执行引擎、本地方法接口和垃圾收集模块
在这里插入图片描述
java从编码到执行
在这里插入图片描述

JVM整体知识认知图

  • JDK是Java程序员常用的开发包、目的就是用来编译和调试Java程序的。
  • JRE是指Java运行环境,也就是我们的写好的程序必须在JRE才能够运行。
  • JVM是Java虚拟机的缩写,是指负责将字节码解释成为特定的机器码进行运行,值得注意的是在运行过程中,Java源程序需要通过编译器编译为.class文件,否则JVM不认识。
    在这里插入图片描述
    在这里插入图片描述

2.创建对象是具体的,类是抽象的

代码如下(示例):

Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
System.out.println("car1="+car1.hashCode());
System.out.println("car2="+car2.hashCode());
System.out.println("car3="+car3.hashCode());

Class<? extends Car> aClass1 = car1.getClass();
Class<? extends Car> aClass2 = car2.getClass();
Class<? extends Car> aClass3 = car3.getClass();
System.out.println("aClass1="+aClass1.hashCode());
System.out.println("aClass2="+aClass2.hashCode());
System.out.println("aClass3="+aClass3.hashCode());

打印信息:
在这里插入图片描述
结论:类是抽象的,创建的对象是具体的

3.类加载器

类加载步骤:
在这里插入图片描述
代码如下(示例):

Car car1 = new Car();

Class<? extends Car> aClass1 = car1.getClass();
System.out.println("aClass1类加载器:"+aClass1.getClassLoader()); //sun.misc.Launcher$AppClassLoader@18b4aac2 应用程序加载器
System.out.println("aClass1的父类类加载器:"+aClass1.getClassLoader().getParent());//sun.misc.Launcher$ExtClassLoader@5b6f7412 扩展类加载器
System.out.println("aClass1的父类的父类类加载器:"+aClass1.getClassLoader().getParent().getParent());//null 1不存在 2找不到 其实是第二种找不到启动类加载器 因为是其他C语言写的

打印信息:
在这里插入图片描述
结论: 自定义加载器 -->app–>Ext–>bootStrap
1、启动类(根)加载器
2、扩展类加载器
3、应用程序加载器
4、自定义加载器

3.1双亲委派机制与作用

3.1.1双亲委派机制:(安全;防止重复加载)

在这里插入图片描述
从上图中我们就更容易理解了,当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。(向上委派,向下加载

总结
①类加载器收到类加载请求;
②将这个请求向上委派给父类加载器去完成,一直向上委派;直到启动类加载器boot;
③启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前加载器;否则就抛出异常,通知子类加载器进行加载;
④重复步骤③
Class Not Found ~
参考

  • https://blog.csdn.net/codeyanbao/article/details/82875064
  • https://www.jianshu.com/p/1e4011617650

3.1.2双亲委派机制的作用

  • 防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
  • 保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象 。这样保证了Class执行安全。

了解~Java历史—沙箱安全机制
参考:
https://blog.csdn.net/qq_30336433/article/details/83268945

4.本地方法栈-Native method stack

public class NativeDemo {
    public static void main(String[] args) {
        new Thread(()->{

        },"my thread").start();
    }

    //native:凡是带了native关键字的,说明Java的作用范围已经达不到了,会去调用地产C语言的库
    //会进入本地方法栈
    //调用本地方法本地接口JNI
    //JNI作用:扩展Java的使用,融合不同编程语言Java所用(最初:C、C++)
    //Java诞生的时候C、C++盛行,想要立足,必须要有调用C、C++的程序
    //它在内存区域中专门准备了一块标记区域:Native Method Stack,登记native 方法
    //在最终执行的时候,加载本地方法库中的方法通过JNI
    private native void hello0();

}

5.程序计数器-PC寄存器

程序计数器:program counter register
每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的=方法字节码(用来存储指向一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计

6.方法区 Method Area

方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单来说,所有定义的方法的信息都存在该区域,此区域属于共享区间;
静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例存在堆内存中,和方法区无关。Static final Class 常量池

创建对象时如何存放???
画出一个对象实例化的过程在内存????

7.Java栈 Java stack

为什么main()方法先执行,最后结束;
栈–>桶(先进后出,后进先出
队列:先进先出FIFO first input first output
喝多了吐是栈;吃多了拉是队列

栈:栈内存主管程序的运行,生命周期和线程同步,线程结束,栈内存也就释放,对于栈来说不存在垃圾回收问题;
在这里插入图片描述
栈主要存储8大基本类型+引用对象+实例的方法
栈运行原理:栈帧 —> 栈满了 stack overflow Error

代码如下(示例):

public class JVMStack {

    public static void main(String[] args){
        JVMStack stack = new JVMStack();
        stack.test1();
    }

    public void test1(){
        test2();
    }

    public void test2(){
        test1();
    }
}

打印信息:
在这里插入图片描述

8.堆heap

一个jvm只有一个堆内存,堆内存的大小是可以调节的;类加载器读取文件后,一般把(对象)什么东西放堆中?
方法区:存放 类、方法、常量及变量~ 堆中:保存我们所有引用类型的真实对象。
堆内存还要细分为三个区域:

  • 新生区(伊甸园区)young/new
    • 伊甸园Eden、辛存区from及辛存区to(from–>to 动态交替的)
  • 老年区old
  • 永久区(元空间)perm
    在这里插入图片描述

8.1新生区

新生区类诞生和成长的地方,甚至是死亡;
伊甸园:所有的对象都是在伊甸园创建new出来的

8.2老年区

①当伊甸园存满且经过轻GC后,没有被GC回收的对象存辛存区to
②当①步把幸存区存满后对象将放入老年区
说明:经过研究验证:99%的对象都是临时对象;说明大部分对象在伊甸园区就被回收;

8.3永久区

这个区域常驻内存,用来存在JDK自身携带的Class对象、interface元数据、存储的是Java运行时的一些环境或类信息,这个区域不存在垃圾回收。关闭VM虚拟机就会释放这个区域的内存~
出现OOM的情况:
一个启动类加载了大量的第三方jar包,Tomcat部署了太多的应用,大量动态生成反射类,不断的被加载
Jdk1.6及以前:有永久区,常量池在方法区
Jdk1.7 :有永久区,但是慢慢退化了,‘去永久区’,常量池在堆中
Jdk1.8及之后:无永久区,常量池在元空间(换名)
在这里插入图片描述
元空间:逻辑上存在,物理上不存在
代码如下(示例):

public class JVMHeap0 {
    public static void main(String[] args) {
        String s = "hahahahaha";
        while(true){
            s += s+new Random().nextInt(222222222)+new Random().nextInt(999999999);
        }
    }
}

打印信息:
在这里插入图片描述
代码如下(示例):

public class JVMHeap1 {
    public static void main(String[] args) {
        long maxMemory = Runtime.getRuntime().maxMemory();
        long totalMemory = Runtime.getRuntime().totalMemory();

        System.out.println("maxMemory="+maxMemory+"字节\t" +maxMemory/1024/1024+"MB");//maxMemory=2863661056字节   2731MB
        System.out.println("totalMemory="+totalMemory+"字节\t" +totalMemory/1024/1024+"MB");//totalMemory=192937984字节    184MB
      }
}

打印信息:
在这里插入图片描述

//默认情况下:分配的总内存是电脑内存的1/4 而初始化的内存1/64
//当配置 -Xms1024m -Xmx1024m -XX:+PrintGCDetails时:

在这里插入图片描述
总结: 305664K+699392K = 1005056k = 981.5MB -->元空间(Metaspace 逻辑上存在,物理上不存在

8.3.1 为什么要废弃1.7中的永久区?

This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation
(since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.

移除永久代是为融合HotSpot JVM与 JRockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代。

8.3.2 对象的访问

  • Java程序会通过栈上的reference数据来操作堆上的具体对象。
  • 主流的访问方式主要有使用句柄和直接指针两种:
    1 使用句柄访问:Java堆中将可能会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,
    而句柄中包含了对象实例数据与类型数据各自具体的地址信息
    在这里插入图片描述
    2 使用直接指针访问:Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference中存储的直接就是对象地址,
    如果只是访问对象本身的话,就不需要多一次间接访问的开销
    在这里插入图片描述
    3 使用句柄来访问的最大好处就是reference中存储的是稳定句柄地址,在对象被移动(垃圾收集时移动对象是
    非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要被修改。
    4 使用直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销。
    5 HotSpot虚拟机采用的是指针访问方式实现。

8.4出现OOM排查:

  • 尝试扩大堆内存看结果:-Xms1024m -Xmx1024m -XX:PrintGCDetails
  • 分析内存,看一下那个地方出现了问题(借助专业工具)
    代码如下(示例):
//-Xms10m -Xmx10m -XX:+PrintGCDetails
public class JVMHeap2 {
    public static void main(String[] args) {
        long maxMemory = Runtime.getRuntime().maxMemory();
        long totalMemory = Runtime.getRuntime().totalMemory();

        System.out.println("maxMemory="+maxMemory+"字节\t" +maxMemory/1024/1024+"MB");
        System.out.println("totalMemory="+totalMemory+"字节\t" +totalMemory/1024/1024+"MB");

        String s = "hahahahaha";
        while(true){
            s += s+new Random().nextInt(222222222)+new Random().nextInt(999999999);
        }
    }
}

打印信息:
在这里插入图片描述
日志分析:
在这里插入图片描述

8.4.1Jprofiler工具使用

Jprofiler 查询哪行代码问题
代码如下(示例):

//-Xms5m -Xmx10m -XX:+HeapDumpOnOutOfmemoryError
public class JVMHeap2 {

    public static void main(String[] args) {
        List<JVMHeap2> list = new ArrayList<JVMHeap2>();

        int count = 0;
        try{
            while(true){
                JVMHeap2 heap2= new JVMHeap2();
                list.add(heap2);
                count = count+1;
            }
        }catch (Error e){
            System.out.println("count = "+count);
            e.printStackTrace();
        }
    }
}

打印信息:
在这里插入图片描述
结合工具分析:
在这里插入图片描述

8.5常用参数说明

上述jvm的参数配置说明:
-Xms5m:设置初始化内存分配大小5m;默认为1/64
-Xmx10m:设置最大分配内存10m;默认1/4
-XX:+PrintGCDetails :打印GC垃圾回收
-XX:+HeapDumpOnOutOfMemoryError :Dump报错信息
-XX:MaxTenuringThreshold=5 :更改辛存区到老年区的阈值

8.6总结

JVM在进行GC时,并不是对这三个区域统一回收,大部分时候回收都是新生代~
常见GC面试题:
JVM的构成模块JVM内存模型和分区;详细到每个区域存放什么?

  • 类加载器子系统,
  • 运行时数据区,
    a.程序计数器(指令的字节码文件)
    b.本地方法栈(登记native方法)
    c.Java栈(栈内存主管程序的运行)
    d.方法区(静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区)
    e.堆(对象)
    新生区
    伊甸园区(新创建的对象)
    辛存区(from)[经过轻GC幸存对象]
    辛存区(to)[经过轻GC幸存对象]
    老年区(幸存区来的对象)
    元空间(自身携带的Class对象、interface元数据、存储的是Java运行时的一些环境或类信息)
  • 执行引擎,
  • 本地方法接口(JNI)
  • 垃圾回收模块
    ②堆里面的分区有哪些?Eden,from,to老年区说说特点?
  • 新生区
    伊甸园区(新创建的对象)
    辛存区(from)[经过轻GC幸存对象]
    辛存区(to)[经过轻GC幸存对象]
  • 老年区(幸存区来的对象)
  • 元空间(自身携带的Class对象、interface元数据、存储的是Java运行时的一些环境或类信息)

9. 垃圾回收

什么是垃圾回收
程序的运行必然需要申请内存资源,无效的对象资源如果不及时处理就会一直占有内存资源,最终将导致内存溢出,所以对内存资源的管理是非常重要了。

内存溢出与内存泄露
内存溢出,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;
内存泄露,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光

Java语言的垃圾回收
为了让程序员更专注于代码的实现,而不用过多的考虑内存释放的问题,所以,在Java语言中,有了自动的垃圾回收机制,也就是我们熟悉的GC。
有了垃圾回收机制后,程序员只需要关心内存的申请即可,内存的释放由系统自动识别完成。换句话说,自动的垃圾回收的算法就会变得非常重要了,如果因为算法的不合理,导致内存资源一直没有释放,同样也可能会导致内存溢出的。
当然,除了Java语言,C#、Python等语言也都有自动的垃圾回收机制。

9.1 什么样的对象需要回收

自动化的管理内存资源,垃圾回收机制必须要有一套算法来进行计算,哪些是有效的对象,哪些是无效的对象,对于无效的对象就要进行回收处理。常见计算无效对象的方法有两种,分别是:引用计数算法、可达性分析算法

9.1.1 引用计数算法

给每个对象分配一个计数器~;计数器本身也会有消耗,所以并不高效不常用
假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用失败时,对象A的引用计数器就-1,如果对象A的计数器的值为0,就说明对象A没有引用了,可以被回收
在这里插入图片描述
优点:
实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。
在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报outofmember 错误。
区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。
缺点:
每次对象被引用时,都需要去更新计数器,有一点时间开销。
浪费CPU资源,即使内存够用,仍然在运行时进行计数器的统计。
无法解决循环引用问题。(最大的缺点)

/**
 * 循环引用中:
 * a和b都为null,但是由于a和b存在循环引用,这样a和b永远都不会被回收
 */
public class T3_CyclicReference {
    public static void main(String[] args) {
         TestA a = new TestA();
         TestB b = new TestB();
         a.b = b;
         b.a = a;
         a=null;
         b=null;

    }
}

class TestA {
    public TestB b;
}

class TestB {
    public TestA a;
}

9.1.2 可达性分析算法

通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过
的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,就说明从GC Roots
到这个对象不可达时,则证明此对象是不可能再被使用的,就是可以回收的对象。
在这里插入图片描述
在JVM虚拟机中,可作为GC Roots的对象包括以下几种:

  • 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
  • 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
  • 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
  • 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
  • Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
  • 所有被同步锁(synchronized关键字)持有的对象。
  • 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

9.1.3 对象的引用

在java中,对象的引用分为:强引用(Strongly Re-ference)、软引用(Soft Reference)、弱引用(Weak
Reference)和虚引用(Phantom Reference

  • 强引用
    在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。
    无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
  • 软引用
    用来描述一些还有用,但非必须的对象。
    只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二
    次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。
  • 弱引用
    用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能存活到下一次垃
    圾收集发生为止。
    当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
  • 虚引用
    最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚
    引用来取得一个对象实例。
    为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。

9.2 垃圾收集算法

9.2.1 GC-复制算法

复制算法的核心就是,将原有的内存空间一分为二,每次只用其中的一块,在垃圾回收时,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾的回收每次GC都会将Eden活的对象移动到辛存区中,一旦Eden区被GC后,就会是空的

优点:
在垃圾对象多的情况下,效率较高
清理后,内存无碎片
缺点:
在垃圾对象少的情况下,不适用,如:老年代内存
分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低

复制算法最佳使用场景:对象存活率低的时候新生区
在这里插入图片描述
JVM中年轻代内存空间

  • 在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。
  • 紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的
    年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移
    动到年老代中,没有达到阈值的对象会被复制到“To”区域。
  • 经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新
    的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区
    域是空的。
  • GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

9.2.3 GC-标记清除

标记清除算法,是将垃圾回收分为2个阶段,分别是标记和清除。
标记:从根节点开始标记引用的对象。
清除:未被标记引用的对象就是垃圾对象,可以被清理。
标记清除法可以说是最基础的收集算法,因为后续的收集算法大多都是以标记-清除算法为基础,对其缺点进行改进而得到的。

优点:不需要额外的空间~
缺点:

  • 执行效率较低,标记和清除两个动作都需要遍历所有的对象,并且在GC时,需要停止应用程序,对于交互性
    要求比较高的应用而言这个体验是非常差的。
  • 通过标记清除算法清理出来的内存,碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以
    清理出来的内存是不连贯的。
    在这里插入图片描述

9.2.4 GC-标记压缩算法

标记压缩算法是在标记清除算法的基础之上,做了优化改进的算法。和标记清除算法一样,也是从根节点开始,对
对象的引用进行标记,在清理阶段,并不是简单的清理未标记的对象,而是将存活的对象压缩到内存的一端,然后
清理边界以外的垃圾,从而解决了碎片化的问题。

缺点:
该算法解决了标记清除算法的碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也
有一定的影响。

总结
内存效率: 复制算法>标记清除算法>标记压缩算法(时间复杂度)
内存整齐度:复制算法=标记压缩算法>标记清除算法
内存利用率:标记清除算法=标记压缩算法>复制算法

9.2.5 GC-分代算法

在堆内存中,有些对象短暂存活有些则是长久存活,所以需要将堆内存进行分代,将短暂存活的对象放到一起,进
行高频率的回收,长久存活的对象集中放到一起,进行低频率的回收,这样才能够更加合理的利系统资源。
分代算法其实就是这样的,根据回收对象的特点进行选择,在jvm中,年轻代适合使用复制算法,老年代适合使用
标记清除或标记压缩算法。
垃圾回收的相关概念:

  • 部分收集(Partial GC)
    新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
    老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。
    混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。
  • 整堆收集(Full GC)

10.JMM

【JMM】(Java Memory Model的缩写)允许编译器和缓存以数据在处理器特定的缓存(或寄存器)和主存之间移动的次序拥有重要的特权,除非程序员使用了volatile或synchronized明确请求了某些可见性的保证。
三大特性:原子性,可见性,有序性。
作用:缓存一致性协议,用于定义数据读写的规则;
JMM定义了线程工作内容与主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory)
在这里插入图片描述
volatile关键字
解决共享对象可见性问题:volilate
两点作用:
1.保证线程间变量的可见性。
2.禁止CPU进行指令重排序。

可见性
volatile修饰的变量,当一个线程改变了该变量的值,其他线程是立即可见的。普通变量则需要重新读取才能获得最新值。
volatile保证可见性的流程大概就是这个一个过程:
在这里插入图片描述
关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完成:

  • lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
  • unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  • read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
  • write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

Java内存模型还规定了在执行上述八种基本操作时,必须满足如下规则:

  • 如果要把一个变量从主内存中复制到工作内存,就需要按顺寻地执行read和load操作, 如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。但Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。
  • 不允许read和load、store和write操作之一单独出现
  • 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。
  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。
  • 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
  • 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现
  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值
  • 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
  • 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。

参考:
https://zhuanlan.zhihu.com/p/29881777
https://zhuanlan.zhihu.com/p/258393139

参考:
https://blog.csdn.net/weixin_33172127/article/details/114038977
https://blog.csdn.net/weixin_33923762/article/details/88030825
https://blog.csdn.net/wangquan1992/article/details/99679969

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值