首页域名资讯 正文

浅析Java虚拟机结构与机制 (上)

2025-02-25 4 0条评论

浅析Java虚拟机结构与机制 (上)

  • 作者:新网
  • 来源:新网
  • 浏览:100
  • 2018-05-11 15:15:39

本文主要介绍JVM的组成部分以及它们内部工作的机制和原理。在研究JVM的过程中会发现,其实JVM本身就是一个计算机体系结构,很多原理和我们平时的硬件、微机原理、 操作系统都有十分相似的地方,所以学习JVM本身也是加深自我对计算机结构认识的一个很好的途径。

   本文主要介绍JVM的组成部分以及它们内部工作的机制和原理。在研究JVM的过程中会发现,其实JVM本身就是一个计算机体系结构,很多原理和我们平时的硬件、微机原理、 操作系统都有十分相似的地方,所以学习JVM本身也是加深自我对计算机结构认识的一个很好的途径。

< div>  一、JVM结构   JVM主要由类加载器子系统、运行时数据区(内存 空间)、执行引擎以及与本地方法接口等组成。其中运行时数据区又由方法区、堆、Java栈、PC寄存器、本地方法栈组成。JVM主要由类加载器子系统、运行时数据区(内存空间)、执行引擎以及与本地方法接口等组成。其中运行时数据区又由方法区、堆、Java栈、PC寄存器、本地方法栈组成。   众所周知,Java语言具有跨平台的特性,这也是由JVM来实现的。更准确地说,是Sun利用JVM在不同平台上的实现帮我们把平台相关性的问题给 解决了,这就好比是HTML语言可以在不同厂商的浏览器上呈现元素(虽然某些浏览器在对W3C标准的支持上还有一些问题)。同时,Java语言支持通过 JNI(Java Native Interface)来实现本地方法的调用,但是需要注意到,如果你在Java程序用调用了本地方法,那么你的程序就很可能不再具有跨平台性,即本地方法 会破坏平台无关性。   二、类加载器子系统(Class Loader)   类加载器子系统负责加载编译好的.class字节码文件,并装入内存,使JVM可以实例化或以其它方式使用加载后的类。 JVM的类加载子系统支持在运行时的动态加载,动态加载的优点有很多,例如可以节省内存空间、灵活地从网络上加载类,动态加载的另一好处是可以通过命名空 间的分隔来实现类的隔离,增强了整个系统的安全性。   1、ClassLoader的分类:   a.启动类加载器(BootStrap Class Loader):负责加载rt.jar文件中所有的Java类,即Java的核心类都是由该ClassLoader加载。在Sun JDK中,这个类加载器是由C++实现的,并且在Java语言中无法获得它的引用。   b.扩展类加载器(Extension Class Loader):负责加载一些扩展功能的jar包。   c.系统类加载器(System Class Loader):负责加载启动参数中指定的Classpath中的jar包及目录,通常我们自己写的Java类也是由该ClassLoader加载。在Sun JDK中,系统类加载器的名字叫AppClassLoader。   d.用户自定义类加载器(User Defined Class Loader):由用户自定义类的加载规则,可以手动控制加载过程中的步骤。   2、ClassLoader的工作原理   类加载分为装载、链接、初始化三步。   a.装载   通过类的全限定名和ClassLoader加载类,主要是将指定的.class文件加载至JVM。当类被加载以后,在JVM内部就以“类的全限定名+ClassLoader实例ID”来标明类。   在内存中,ClassLoader实例和类的实例都位于堆中,它们的类信息都位于方法区。   装载过程采用了一种被称为“双亲委派模型(Parent Delegation Model)” 的方式,当一个ClassLoader要加载类时,它会先请求它的双亲ClassLoader(其实这里只有两个ClassLoader,所以称为父 ClassLoader可能更容易理解)加载类,而它的双亲ClassLoader会继续把加载请求提交再上一级的ClassLoader,直到启动类加 载器。只有其双亲ClassLoader无法加载指定的类时,它才会自己加载类。   双亲委派模型是JVM的第一道安全防线,它保证了类的安全加载,这里同时依赖了类加载器隔离的原理:不同类加载器加载的类之间是无法直接交互的,即 使是同一个类,被不同的ClassLoader加载,它们也无法感知到彼此的存在。这样即使有恶意的类冒充自己在核心包(例如 java .lang)下,由 于它无法被启动类加载器加载,也造成不了危害。   由此也可见,如果用户自定义了类加载器,那就必须自己保障类加载过程中的安全。   b.链接   链接的任务是把二进制的类型信息合并到JVM运行时状态中去。   链接分为以下三步:   a.验证:校验.class文件的正确性,确保该文件是符合规范定义的,并且适合当前JVM使用。   b.准备:为类分配内存,同时初始化类中的静态变量赋值为默认值。   c.解析(可选):主要是把类的常量池中的符号引用解析为直接引用,这一步可以在用到相应的引用时再解析。   c.初始化   初始化类中的静态变量,并执行类中的static代码、构造函数。   JVM规范严格定义了何时需要对类进行初始化:   a、通过new关键字、反射、clone、反序列化机制实例化对象时。   b、调用类的静态方法时。   c、使用类的静态字段或对其赋值时。   d、通过反射调用类的方法时。   e、初始化该类的子类时(初始化子类前其父类必须已经被初始化)。   f、JVM启动时被标记为启动类的类(简单理解为具有main方法的类)。   三、Java栈(Java Stack)   Java栈由栈帧组成,一个帧对应一个方法调用。调用方法时压入栈帧,方法返回时弹出栈帧并抛弃。Java栈的主要任务是 存储方法参数、局部变量、中间运算结果,并且提供部分其它模块工作需要的数据。前面已经提到Java栈是线程私有的,这就保证了线程安全性,使得程序员无 需考虑栈同步访问的问题,只有线程本身可以访问它自己的局部变量区。   它分为三部分:局部变量区、操作数栈、帧数据区。   1、局部变量区   局部变量区是以字长为单位的数组,在这里,byte、short、char类型会被转换成int类型存储,除了long和 double类型占两个字长以外,其余类型都只占用一个字长。特别地,boolean类型在编译时会被转换成int或byte类型,boolean数组会 被当做byte类型数组来处理。局部变量区也会包含对象的引用,包括类引用、接口引用以及数组引用。   局部变量区包含了方法参数和局部变量,此外,实例方法隐含第一个局部变量this,它指向调用该方法的对象引用。对于对象,局部变量区中永远只有指向堆的引用。   2、操作数栈   操作数栈也是以字长为单位的数组,但是正如其名,它只能进行入栈出栈的基本操作。在进行计算时,操作数被弹出栈,计算完毕后再入栈。   3、帧数据区   帧数据区的任务主要有:   a.记录指向类的常量池的指针,以便于解析。   b.帮助方法的正常返回,包括恢复调用该方法的栈帧,设置PC寄存器指向调用方法对应的下一条指令,把返回值压入调用栈帧的操作数栈中。   c.记录异常表,发生异常时将控制权交由对应异常的catch子句,如果没有找到对应的catch子句,会恢复调用方法的栈帧并重新抛出异常。   局部变量区和操作数栈的大小依照具体方法在编译时就已经确定。调用方法时会从方法区中找到对应类的类型信息,从中得到具体方法的局部变量区和操作数栈的大小,依此分配栈帧内存,压入Java栈。

文章版权及转载声明

本文作者:亿网 网址:https://edns.com/ask/post/149448.html 发布于 2025-02-25
文章转载或复制请以超链接形式并注明出处。