那么什么叫做Java虚拟机呢?
我们学习的时候,应该都知道Java语言能够一次编译,到处运行的特点,其中这种平台无关性关键就在于Java虚拟机,他是一个可执行Java字节码的虚拟机进程。
组成部分
ClassLoader:(类加载器)类装载子系统,JVM启动的时候,将字节码加载到JVM中。
RunTime Data Area:(运行时数据区)如上图,将内存分为不同的区域,分别实现不同的功能。
Excution Engine:(执行引擎)不仅负责执行class文件中的字节码指令,还会调用GC操作。
Native Interface:(本地接口)融合不同开发语言的原生库(主要为C和C++)为Java所用。
类加载器的加载过程 (主要的就为5个部分,原为7个部分,不过最后两个部分我觉得并没有什么,比较好理解)
类加载过程中,在解析阶段,JVM会将二进制常量池内的符号引用替换为直接引用。
符号引用:符号引用以一组符号来描述所引用的目标,符号可以试任何形式的字面量,只要使用时能无歧义的定位到目标位置就行。(存放在方法区中的运行时常量池中)
**直接引用:**直接引用就是可以直接指向目标的指针、相对偏移量或者时是一个能间接定位到目标的句柄。
我们都知道,在程序运行过程中,程序会将java 文件转化为.class文件,将.class文件加载到内存中,将其放在运行时数据区的方法区内。这个过程就是类加载过程。那么是怎么被加载的呢? 这里就运用了双亲委派模型
**启动类加载器(BootStrapClassLoader):**是最顶层的类加载器,主要加载核心类库
**扩展类加载器(ExtClassLoader):**加载
**应用程序类加载器(AppClassLoader):**负责加载用户路径下的类加载器
Launcher是classloader的入口,扩展类加载器和应用程序类加载器是一个静态内部类(并不是我们想象中认为的父类加载器(继承下来的),源码中只是存在一个指针有存放上一级的关系而已,有兴趣搜一下Launcher类类去看一下,更好的理解哦),继承于URLclassLoader,间接继承classLoader。
简单的说,就是当一个类需要被加载的时候,会先去查看是否已经被加载过了,如果没有被加载,则会请求委派给父类,第一层父类就是应用程序类加载器AppClassLoader,检查这个类是否已经加载,没有加载,则继续请求委派给父类,接下来就是扩展类加载器,同样,继续直到最顶配的启动类加载器,当所有的父类都无法完成这个加载请求的时候,才会交给子加载器尝试加载
总所周知,java.lang.Object类是所有类的父类,程序在运行期间会把java.lang.Object类加载到内存当中,假如java.lang.Object能够被我们自定义类加载器去加载的话,那么JVM中就会存在多份Object的Class对象,而且这些对象是不兼容的。
所以双亲委派模型可以保证java核心类库的类型安全
借助双亲委派模型,我们java核心类库的类必须是由我们的启动类加载器加载的,这样可以确保我们核心类库只会在jvm中存在一份,这样就不会给自定义类加载器去加载我们核心类库的类。
一个小小的问题 ------> JVM是如何判定两个类是相同的?
主要还是判断是否是同一个类加载器加载出来的,即便是相同全限定类名的类,通过不同的类加载器加载出来的都是不一样的class对象
内存区又可分为线程共享区和私有区 栈管运行,堆管存储
局部变量表:存放一组变量值的存储空间,用于存放方法参数和局部变量的信息
操作数栈:用于计算的临时数据区
动态链接:符号引用在运行过程中转化为直接引用,类加载过程中的解析过程。 符号引用存在于方法区中的运行时常量池 ,每一个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,目的就是为了支持方法在调用过程中的动态链接。
方法出口: 在方法完成之后,都需要返回最初方法被调用时的位置,程序才能正常秩序,所以会在栈帧中存储方法返回的地址,用来帮助程序返回。