JVM的体系结构及底层原理(满满干货,放心食用~)

本文详细介绍了JVM的体系结构,包括类装载器的双亲委派机制、本地方法栈、程序计数器、方法区、栈和堆的运作原理。此外,还讨论了堆的内存管理,如Minor GC和Major GC,以及如何避免堆溢出错误。通过对JVM的深入理解,有助于优化Java应用的性能。
摘要由CSDN通过智能技术生成

1. JVM体系结构

首先,我们来了解 JVM 的位置,如下图,JVM 是运行在操作系统之上的,它与硬件没有直接交互。
在这里插入图片描述
接下来是 JVM 的整个体系结构图:
在这里插入图片描述
然后,我们根据 JVM 体系结构图来阐述相关的知识点。

2. 类装载器 ClassLoader

类装载器 ClassLoader 是负责加载class文件的,将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构。ClassLoader只负责文件的加载,至于它是否可运行,则由Execution Engine决定。

在这里需要区分一下classClass。小写的class,是指编译 Java 代码后所生成的以.class为后缀名的字节码文件。而大写的Class,是 JDK 提供的java.lang.Class,可以理解为封装类的模板。多用于反射场景,例如 JDBC 中的加载驱动,Class.forName("com.mysql.jdbc.Driver");

接下来我们来观察下图,Car.class字节码文件被ClassLoader类装载器加载并初始化,在方法区中生成了一个Car Class的类模板,而我们平时所用到的实例化,就是在这个类模板的基础上,形成了一个个实例,即car1car2。反过来讲,我们可以对某个具体的实例进行getClass()操作,就可以得到该实例的类模板,即Car Class。再接着,我们对这个类模板进行getClassLoader()操作,就可以得到这个类模板是由哪个类装载器进行加载的。
在这里插入图片描述

Tip:扩展一下,JVM并不仅仅只是通过检查文件后缀名是否是.class来判断是否加载,最主要的是通过class文件中特定的文件标示,即下图test.class文件中的cafe babe在这里插入图片描述

2.1 有哪些类装载器?

(1)虚拟机自带的加载器

  1. 启动类加载器(Bootstrap),也叫根加载器,加载%JAVAHOME%/jre/lib/rt.jar
  2. 扩展类加载器(Extension),加载%JAVAHOME%/jre/lib/ext/*.jar,例如javax.swing包。
  3. 应用程序类加载器(AppClassLoader),也叫系统类加载器,加载%CLASSPATH%的所有类。

(2)用户自定义的加载器 :用户可以自定义类的加载方式,但必须是Java.lang.ClassLoader的子类。
在这里插入图片描述

2.2 双亲委派和沙箱安全

接下来,我们通过下面代码来观察这几个类加载器。首先,我们先看自定义的MyObject,首先通过getClassLoader()获取到的是AppClassLoader,然后getParent()得到ExtClassLoader,再getParent()竟然是null?可能大家会有疑惑,不应该是Bootstrap加载器么?这是因为,BootstrapClassLoader是使用C++语言编写的,Java在加载的时候就成了null。

我们再来看Java自带的Object,通过getClassLoader()获取到的加载器直接就是BootstrapClassLoader,如果要想getParent()的话,因为是null值,所以就会报java.lang.NullPointerException空指针异常。
在这里插入图片描述

输出中,sun.misc.Launcher是JVM相关调用的入口程序。

那为什么会出现这个情况呢?这就需要我们来了解类加载器的加载顺序和机制了,即双亲委派沙箱安全

(1)双亲委派,当一个类收到了类加载请求,它首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,因此所有的加载请求都应该传送到启动类加载器中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。

采用双亲委派的一个好处是,比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委派给顶层的启动类加载器进行加载,确保哪怕使用了不同的类加载器,最终得到的都是同样一个Object对象。

(2)沙箱安全机制,是基于双亲委派机制上采取的一种JVM的自我保护机制,假设你要写一个java.lang.String的类,由于双亲委派机制的原理,此请求会先交给BootStrapClassLoader试图进行加载,但是BootStrapClassLoader在加载类时首先通过包和类名查找rt.jar中有没有该类,有则优先加载rt.jar包中的类,因此就保证了java的运行机制不会被破坏,确保你的代码不会污染到Java的源码

所以,类加载器的加载顺序如下:

  1. AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
  2. ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
  3. 如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载。
  4. ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException

Tip:rt.jar是什么?做了哪些事?这些暂且不提,那你有没有想过,为什么可以在idea这些开发工具中可以直接去使用String、ArrayList、甚至一些JDK提供的类和方法?观察下面动图就可以知道,原来这些都在rt.jar中定义好了,且直接被启动类加载器进行加载了。
在这里插入图片描述

3. 本地方法栈 Native Method Stack

本地方法接口(Native Interface),其作用是融合不同的编程语言为 Java 所用,它的初衷是用来融合 C/C++ 程序的,Java 诞生的时候是 C/C++ 流行时期,要想立足,就得调用 C/C++ 程序,于是 Java
就在内存中专门开辟了一块区域处理标记为 native 的代码。

本地方法栈(Native Method Stack),就是在一个 Stack 中登记这些 native 方法,

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值