JVM内存模型

什么是JVM

Java Virtual Machine,Java虚拟机;java的跨平台性是之java编译出来的字节码文件可以在任何装有java虚拟机的电子设备上运行,java虚拟机中的java解释器负责将字节码文件解释成特定的机器码运行,在运行时,java编译器负责将java源程序编译成.class文件,java.exe就是.class的执行程序,因为它装载了jvm.dll文件,这个动态链接库才是java虚拟机的实际操作所在。
在这里插入图片描述

JVM内存区域的划分

粗略的看,jvm大概可以分为三个部分,类装载器子系统、运行时数据区、执行引擎
1、类装载器子系统
当java编译器把源代码编译成字节码.class文件之后,再把字节码装载到jvm中去,映射到各个内存区域,程序就可以执行了,但是字节码文件的装载也需要很多的步骤完成。
第一步:加载,加载是类加载的第一个阶段,虚拟机需要完成三件事情;通过类的全限定名来获取类的定义此类的二进制字节流;将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;在内存中生成一个代表这个类的java.lang.class对象,作为方法区这个类的各种数据的访问入口。
【注意:数组本身不通过类加载器创建,它是由jvm虚拟机直接创建,但是数组里面的各种元素是由类加载器创建】

第二步:验证,验证的目的是为了确保.class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会损害虚拟机自身的安全。验证分多个方面;文件格式的验证,是否是以魔术0xCAFEBABE开头,是否存在不被支持的常量类型;元数据验证,是否有父类,是否继承了步被允许继承的类;字节码验证,判断字节码的语义是否完整,符合逻辑;符号引用验证,方法的访问权限是否可以被访问。

第三步:准备,主要是为变量分配内存设置初始值,但是此时的变量只包含被static修饰的变量,并不包含实例变量,实例变量是根据对象在被实例化的时候随着对象一起java堆中,此时的初始值基本类型为0;引用类型为null;被final修饰的变量会被直接赋实际值。

第四步:解析,将符号引用转化为直接引用的关系,例如某一个类继承了Object这个类,之前的符号引用就是记录了一个‘object’,但是根据这个符号是不能找到此类的内存地址的,所以要变为直接引用,根据地址可以准确的找到此类。

第五步:初始化,真正的开始执行类中定义的java程序代码(字节码)。在初始化过程中,如果一个类有父类,那么首先会初始化其父类的,因为Objet是所有的类的父类,所以Objet一定会先被初始化。

以上便是类的加载过程,那么我们再说一下这个过程中的主角:类加载器
类加载器ClassLoader,它是一个抽象类。ClassLoader的具体实例负责把java字节码读取到内存当中去。此类中的重要的方法有:
在这里插入图片描述
载入并返回一个类。
在这里插入图片描述
定义一个类,此方法不公开。
在这里插入图片描述
查找一个类。
在这里插入图片描述
查找一个已被加载的类。

系统中类加载器分4种:
BootStrap Classloader (启动ClassLoader)
Extension ClassLoader (扩展ClassLoader)
App ClassLoader (应用ClassLoader)
Custom ClassLoader (自定义的ClassLoader)

每一个ClassLoader都有一个父类的ClassLoader,除了BootStrap ClassLoader之外
ClassLoader加载机制如下:
在这里插入图片描述
一般类的加载器分为三种:
第一种:全盘负责,当一个类加载器去加载一个class时,这个class依赖的其他class也被次类加载器加载。
第二种:双亲委派,首先会从App ClassLoader中调用findLoaderClass方法查看是否已经被加载,如果没有,则会交给父类,Extension ClassLoader去查看是否被加载,如果没有,则调用父类;bootstrap ClassLoader查看是否已经加载,如果没有,则尝试从 Bootstrap ClassLoader到 App ClassLoader依次加载。这个过程就叫做双亲委派模式。
第三种:缓存机制,缓存机制保证已经被加载的类都会被缓存,当程序需要使用某一个class时,先从缓存中获取,如果缓存中不存在,系统才会读取该类对应的二进制数据,并将其转化成class对象放入缓存中,因此在修改了class之后必须重启jvm,class才会生效。
双亲委派模式的好处:
采用这种方式,java类随着类加载器也变的有层级,而且避免了类的重复加载。
双亲委派模式的问题:
顶层的加载器无法访问加载底层的ClassLoader的类,解决办法就是在顶层的ClassLoader中,传入底层的ClassLoader的实例。

2、运行时数据区
java虚拟机在执行java程序的过程中会把它管理的内存区域分为多个模块分布管理,每一个区域的内存各不相同。
java主要的内存区域分为5个:

程序计数器: 多线程中,cpu轮流切换线程来执行,程序计数器主要就是记录线程执行的机器码的位置,方便cpu切换时能够准确的知道该从哪一行机器码执行。线程私有并且不会产生OOM。

java堆: 线程共享,多线程请注意,主要存放对象的实例,就是通过new来获取的对象实例;GC的主要战场,会产生OOM。【GC机制请看下篇文章】

虚拟机栈: 每一个线程在执行的时候都会创建一个栈帧,用于存放局部变量表、操作数栈、动态链接、方法出口等信息。
局部变量表:主要存储编译期的各种基本数据类型,对象的引用和方法的参数。
操作数栈:当一个方法开始执行的,操作栈时空的,执行过程中会有各种字节码指令在栈中写入和提取,就是入栈出栈的过程。
动态连接:将符号引用转化为直接引用,一些符号引用在加载阶段就转化为了直接引用,还有一部分在运行期间才能转化为直接引用。
方法出口:也叫做方法返回地址,一个方法执行的时候只有2种方法可以退出这个方法,第一种就是执行引擎遇到了返回的字节码指令;另一种就是产生异常;但是不管怎么样,在方法退出之后,都需要返回到方法被调用的位置。

本地方法栈: 这里面存放的都是jvm的本地方法服务。

方法区: 线程共享,多线程请注意,它主要存储已被加载的类信息,类中常量,类中静态变量,类中的方法信息,我们在方法中使用getName、isInterface等方法来获取信息时,主要就是从这个区域获取。

3、执行引擎
它或者在执行本地方法,或者在执行字节码,
第一代jvm的执行技术:解释
第二代jvm的执行技术:即时编译
自适应优化:目前sun公司使用的虚拟机执行技术,结合了前2代;开始对所有的代码都采取解释执行的方式,并监视代码执行的情况,对于一些被频繁调用的代码,后台会启动一个线程去将其编译为本地代码,并优化,如果长时间不调用了 ,则取消编译的代码,仍其解释执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值