JVM内存模型

Jvm即java虚拟机,是Java Virtual Machine的缩写,jvm向Java程序员提功了一套,统一的接口,所以Java程序要在不同的平台上运行只要,安装对应的jvm即可。
当一个类需要被加载时,.java文件会被编译成.class文件,然后被加载进jvm中,加载的过程中会使用到双亲委派机制。一个自定义的类被加载的时候会向他的父类加载器委托,一直到启动类加载器,如果启动类加载器中有这个类,就在启动类加载器中加载,如果不是就交给它的子类加载器加载。(类加载的时机:一、创建一个类的实例,也就是new一个类的时候。二、访问类的静态变量。三、访问类的静态方法。四、反射,Class.forName。五、初始化一个类的子类。六、虚拟机启动时,定义了main()方法的那个类)
启动类加载器加载的是jre和jre/lib目录下的核心库,中的方法主要是用C++编写的,无法看到源码。
扩展类加载器加载的是jre/lib/ext目录下的扩展包,就是jdk中自带的方法。
应用程序类加载器加载当前java工程的bin目录,也就是我们自己的Java代码编译成的class文件所在。
自定义加载器
在这里插入图片描述类加载的过程有七步加载、验证、准备、解析、初始化、使用、卸载
其中,验证、准备、解析这3个部分统称为连接
在这里插入图片描述
加载:
一、通过类的全限定名来获取定义此类的二进制字节流,
二、将该二进制字节流所代表的静态存储结构转化为方法区的运行时数据结构,该数据存储数据结构由虚拟机实现自行定义,
三、在内存中生成一个代表这个类的java.lang.Class对象,它将作为程序访问方法区中的这些类型数据的外部接口
验证:
是连接阶段的第一步,且工作量在JVM类加载子系统中占了相当大的一部分目的:为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
VM能否承受恶意代码的攻击,因此验证阶段很重要,但由于它对程序运行期没有影响,并不一定必要,可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。
准备:为类变量(静态变量)分配内存:因为这里的变量是由方法区分配内存的,所以仅包括类变量而不包括实例变量,后者将会在对象实例化时随着对象一起分配在Java堆中设置类变量初始值:通常情况下零值
解析:解析阶段就是虚拟机将常量池内的符号引用替换为直接引用的过程
初始化:是类加载过程的最后一步,会开始真正执行类中定义的Java代码。而之前的类加载过程中,除了在『加载』阶段用户应用程序可通过自定义类加载器参与之外,其余阶段均由虚拟机主导和控制
clinit:由编译器自动收集类中的所有类变量(静态变量)的赋值动作和静态语句块static{}中的语句合并产生,存放在元空间中。
https://baijiahao.baidu.com/sid=1662931890502685014&wfr=spider&for=pc这篇文章讲的非常详细
在这里插入图片描述
Jvm一共有五个区域:栈,本地方法栈,程序计数器(这三个是线程私有的),堆,元空间(1.8之前交方法区或永久代)(这两个是线程共有的)

元空间(Metaspace):在jdk1.8之前被称为永久代、方法区,在1.8之前永久代是在堆内存上开辟了一部分区域,在1.8之后不在放到堆中,而是使用直接内存(电脑的内存)。1.8之后的元空间存放的内容有:1…class文件的基本信息,包括类,接口,枚举,注解。2.运行时常量池(String常量池被放在了堆中)3.JIT代码机器指令缓存。
类的基本信息有:
1.实现的是哪个父类,实现的哪些接口,那个类加载器加载的。
2.域的信息,即属性的信息,有类型,public还是private等信息。
3.方法信息,方法的实现方法,方法返回值,方法参数等信息。
运行时常量池:
编译class 文件池中有常量池,对应的是记录该类用到的字面量(1.文本字符串。2.八大基本类型的值。3.被声明为final的常量等),引用,和方法调用,我们称为静态常量池。
作用是:一个类有父类和实现了很多接口,调用了很多方法,不能全部将这些类都写入到class文件中,所以需要记录符号引用。
运行时常量池,是类加载器在加载的过程中,将class的静态常量池加载到方法区/元空间里面,不同的需要将符号引用,转化成实际引用,可以动态扩展。
本地方法栈(Native Method Stacks):C/C++编写的程序,可以通过native调用
程序计数器((Program Counter Register):当前线程正在执行的字节码的地址,每一个线程在工作的时候都有一个独立的计数器。
栈(stack):本质是一种数据结构,栈的特点是先进后出,java程序在运行时是在栈这种数据结构中进行的,每有一个main方法就是一个栈,main方法启动时就会将main方法压如栈底(称为栈帧,当前正在执行的方法称为当前栈帧),当方法需要调用其它方法时,就会通过地址值,到元空间中读取这个方法,然后再将这个方法压入栈内,当这个方法运行结束时,就会出栈。栈中有很多个栈帧,每一个站大致又包括五个部分,分别为:局部变量表,操作数栈,动态链接,返回值地址,其他信息。
局部变量表:存储这个方法的局部变量,还有一点很重要自增和自减是在局部变量表中进行的。
操作数栈:操作数栈是对数据进行操作的地方,比如加减乘除。具体的下文会进行分析。
动态链接:Class 文件中存放了大量的符号引用,字节码中的方法调用指令就是以常量池中指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或第一次使用时转化为直接引用,这种转化称为静态解析。另一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。
返回值地址:方法在执行完成后需要返回的方法运行的位置。
下面将通过分析为什么
int i = 1;
i = i++;
输出i,i的值为1。
编写一个测试类
在这里插入图片描述
打开cmd用javac 加文件名进行编译在这里插入图片描述
在使用javap -c 加文件名 将.class文件进行反编译得到
在这里插入图片描述
找到main方法,这就是进入main方法后jvm执行的内容。这些字节码指令和的含义在网上有很多。
iconst_1:表示将int类型的1加载到操作数栈。
istore_1:表示将int类型的1存储到局部变量表。在这时局部变量表中会创建i来接受这个1.
iload_1:表示将int类型的1从局部变量表中复制一份到操作数栈中
iinc:表示局部变量表中的i自增1
istore:这时操作数栈的1会顶掉局部变量表中i自增后的2,被存储到存储到局部变量表中。
所以i = i++; 最终结果为1。
堆(Heap):jdk1.8时GC垃圾回收器默认使用的时CMS,这时堆中被分为了年轻代(Young generation)和老年代(Old Generation),年轻代和老年代的比例大概为1:2。其中年轻代又分为伊甸区(Eden)和两个幸存区(Survivor),两个幸存区也叫from区和to区。当一个对象被实例化时会在Eden区出生,当Eden放区满的时候就会触发轻量级的GC(minor GC),这时使用的方法是标记复制法,将存活的对象放到from区,当from区放满时,也会触发minor GC,这时候会将存活的对象放入to区(并且年龄加1),这时候from区和to区会发生互换。当年龄超过一定值时会被放到老年代(默认是15)。老年代除了存放从年轻代过来的对象,还会存放占用空间很大的对象。当老年代放满时会触发重量级Full GC,Full GC会stop the world 停止java全部线程全力执行GC,这会浪费大量GC。Jvm调优的目的就是减少Full GC触发的次数从而提高程序运行效率。
在这里插入图片描述
在jdk9之后默认使用的垃圾回收器是G1,G1和CMS最大的区别就是CMS他的各个区域是一片整体,而G1将整个堆分为了若干个,大小相等的区域,在这个区域没有被使用时,什么区都不是。当一个对象被实例化时,如果Eden区满了,就会去堆中申请一个区域这个区域就变成了Eden区,其他区域同理。还有一点CMS回收器很大的对象是直接被放在老年代中,而在G1中新加了一个区Humongous区,这是专门用来存一个区域无法放满的对象。其他的都和CMS一样。
在这里插入图片描述
类的实例对象存在堆空间中,大致分为三个部分,对象头(占12Byte),实例数据,对其填充(存储的大小必须为8的倍数,当没有存满是会自动补齐)。
对象头中就包含存储对象自身的运行时数据(Mark Word占8Byte),类型指针。Mark Word中就包含哈希吗,GC的分代年龄,锁状态标识,线程持有的锁,偏向线程ID,偏向时间戳。
哈希吗:根据内存的内部地址(物理存储地址)计算得出。
GC分代年龄:最大值为15,应为存储大小为4位,1111二进制换算就是15。
锁状态标识:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
JMM的全称是Java Memory Model(Java内存模型)
JMM的关键技术点都是围绕着多线程的原子性、可见性和有序性来建立的,这也是Java解决多线程并行机制的环境下,定义出的一种规则,意在保证多个线程间可以有效地、正确地协同工作。
JMM数据原子操作有:read、load、use、assign、store、write、lock、unlock
JMM缓存不一致问题如下图所示
在这里插入图片描述
解决方法有两种:
1.总线枷锁,但是性能太低。
2.MESI缓存一致性协议,总线嗅探机制,一旦其他线程将总线中的值改变后就会把工作空间中的缓存置为无效,这样就会重新读取。
在这里插入图片描述
会在store之后上锁,在write之后解锁。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值