JVM与其结构方式

一:介绍与结构

JVM,俗称java虚拟机,已知JRE为java文件提供运行时的必要环境,且JRE中包含了JVM,则JVM同为java文件运行时所必备的温床。

Java虚拟机本质上就是一个程序,只不过其面向的对象被限定为了java文件,JVM带有VM二字,类比于VMWare,VMWare可以支持在同一台计算机上运行两个不同的系统,而实际上第二个系统是依赖于虚拟机存在的,可以与本机上的软件并行不悖,故JVM也可以做到与本机上的其他程序并行执行,同时又提供单方面的java运行环境服务。

结构图:
在这里插入图片描述

二:类加载器

其中,类加载器的主要作用是将字节码文件加载到内存然后进行初始化,继而产出一个class对象,注意这里的class跟前面的class字节码文件是两个不同的东西,.class文件本身是一种物的抽象,其本质是文件,如数学公式一般提供的是一种追求泛用性的标准,类加载器需要依赖这种标准进行加载,但要实现某种业务逻辑所需要的还是具体的物,因此需要建立与类相关的class对象,然后在对象中进行具体的,可取舍的赋值操作,而且这里建立的class对象就是将来java的反射所要操作的class。

这个建立class对象的过程就是new 对象的过程,new出来的对象其hash值是不相同的,因为具体的物具备其独有的特殊性,而new对象需要一个名字,例如Person p = new Person(),其中p这个名字是存储在栈中的,但也仅仅是徒有其名,而程序员new出的对象实际是保存在堆中,因此栈中存储的重要之物实际为堆中的引用地址。

与此同时,虽然new出的对象其hash是各不相同的,但如果使用具体的getClass方法来获取其class对象的hash的话,那这个父级别的hash就是统一的,因为类加载器所构造的这个class对象时全局唯一的,而对象的new这一说也是根据这个唯一的class标准来进行后续的补充建立的。

当然,类加载器的种类也不是单一的,常见的有应用程序加载器(系统类加载器),扩展类加载器,根加载器,其他的有虚拟机自带的加载器等。

其中根加载器是java最底层的加载器,加载器底层是用c编写,因此在java中无法用getClassLoader获取其具体的信息,这不是说加载器本身并不存在,而是在此处彰显了某种形式的生殖隔离。

再有一个是扩展类加载器,对应的是我们jre lib下的ext文件夹,其优先级略低于java的根加载器。

最后一个是应用程序加载器,也是最常见的加载器,我们主要补充并扩展的具体类就是由应用程序加载器来操作完成的。

而既然存在这么多的加载器,根加载器默认找我们的jre lib rt.jar包,ext找我们的扩展类文件包,而应用程序加载器负责找我们自己定义的具体的加载器,但这样的分工明确在不附加任何限制条件的情况下就会产生执行歧义,即假如用户自己定义了一个java.lang.Integer类,并且用应用程序加载器去执行,那系统到底是走底层的java.lang.Integer还是走自定义的java.lang.Integer就会产生问题,如果是走的用户自定义的Integer,那就会把原来的Integer给覆盖掉,因此就会产生更多的麻烦。

而为了避免产生此类麻烦,JVM就被赋予了与之相对应的某种机制,即双亲委派机制。

  • 双亲委派机制

即JVM在使用应用程序构造器执行一个类的时候,并不会直接运行,而是向上推送,先是去查看根加载器中是否有与之重复的类,如果有,那执行权交给扩展根加载器,然后根加载器执行的是已有的该类。如果根加载器没有,才开始向下寻找,走扩展类加载器,扩展类加载器中存在这样的类,执行权交给扩展类加载器,扩展类加载器执行自己已有的该类。假如说寻找了一圈上层的加载器都不存在该具体的类,才会最终把执行权分发给下属加载器,由对应的应用程序加载器来执行类的构造。

  • 沙箱安全机制

沙箱即程序运行的限制环境。

参考:https://blog.csdn.net/qq_30336433/article/details/83268945

沙箱组成:

字节码校验器:验证是否符合java语言规范,同时java核心类不会通过字节码校验器进行元素审核。

类加载器:隔离恶意代码,守护信任类库的边界,归入保护域明确代码职责

存取控制器:存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。

安全管理器:是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。

安全软件包:java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性



三:Native方法区

首先,JVM的功能与交流是双向的,如vue中的MVVM,其中VM即负责对View和Model的双向沟通,而这里的JVM同样也可以向上服务于java环境,从而提供一个可靠而安全的java运行胚床,向下扎根于操作系统,提供一些简洁的接口与计算机底层进行交互。

因此,在某些需要跟底层打交道的方法中会存在带有native关键字的方法,比如Thread,这是因为,此类任务的处理权限已经超出了java的管辖权限,但为了圆满且有效的完成任务,JVM于是提供了一些双向的JNI(Java Native Interface),这些接口来处理对计算机底层的面向。
在这里插入图片描述

然后回到JVM执行结构,与Native有关的有三个东西:本地方法栈,本地方法接口,本地方法库。

在这里插入图片描述
如Thread类中的start0方法就登记在本地方法栈中,JVM执行的时候首先来到本地方法栈找工具,然后本地方法栈中的方法借助链接本地库的JNI沟通本地方法库,例如java想要兼容一些c的文件,那就需要通过JNI来做一下兼容处理,而本地方法库中真实存在着那些链接计算机底层的工具,然后才能完成java的扩展使用。

本地方法栈:只负责登记native相关
本地方法接口:沟通j兼容ava外部文件
本地方法库:真正执行native方法的地方

但值得一提的是,native虽然能够有效的与硬件层连接,但本身并不是一种值得推荐的方法,只有在别无选择的时候才会采用。


四:程序计数器

也叫pc寄存器,是计算机处理器中的寄存器,它包含当前正在执行的指令的地址(位置)。当每个指令被获取,程序计数器的存储地址加一。在每个指令被获取之后,程序计数器指向顺序中的下一个指令。当计算机重启或复位时,程序计数器通常恢复到零。



五:new对象的内存执行

在这里插入图片描述
例如:Person p =new Person();

已知该语句是new出了一个Person的实例,那接下来就会引申出三个问题,new对象从哪儿new?new出的p储存在哪儿?储存Person实体的实际内存开辟在哪儿?

第一个问题:new对象从哪儿new?
首先根据上图所知,字节码文件到类加载器后生成class对象,该class对象全局唯一,同时也充当Person这个类的全局模板,new实例所依赖的模板也是从这里来的。

第二个问题:new出的p储存在哪儿?
p仅仅是一个对象变量的名字,同时,比起名字这里更类似于标记,因为p在这里只是徒有其名,因为p的本质是一个引用,引用的什么?引用的在堆中的地址。

第三个问题:储存Person实体的实际内存开辟在哪儿?
在堆中,栈是面向java的,储存基本数据类型及其数值,还有就是引用对象的变量,以及其在堆中的引用地址,而堆是面向程序员的,new出来的实例实际存在堆中,因此堆中会产生并存在垃圾,而栈因为存在栈帧可以释放内存所以不存在垃圾,因此堆才是JVM调优的重点调优地点。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Deeeelete

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

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

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

打赏作者

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

抵扣说明:

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

余额充值