【JVM】深入理解类加载机制(二)

深入理解类加载机制

HSDB工具的使用

在这里插入图片描述

Hotspot Debugger(HSDB):JDK原生自带
以Windows系统为例,jdk8的环境,在jdk的lib目录下,启动之前,你需要确保你进入的lib目录和你当前的JAVA_HOME配置的JDK是相同的,否则可能会出现无法加载libarary的异常,进而无法使用HSDB,命令如下

java -cp .\sa-jdi.jar sun.jvm.hotspot.HSDB

调节字体大小的方法,添加环境变量JAVA_TOOL_OPTIONS

-Dswing.plaf.metal.controlFont=Dialog-22 -Dswing.plaf.metal.systemFont=Dialog-22 -Dswing.plaf.metal.userFont=SansSerif-22

在这里插入图片描述

这里需要用attach到一个java进程,
在这里插入图片描述
利用jps可以查看到相关指令
在这里插入图片描述
将进程号输入进去,我这里换了一个程序,进程号不同
在这里插入图片描述
会看到运行的Java Thread
在这里插入图片描述
在这里插入图片描述

查看main线程的调用栈

静态字段如何存储

instanceKlass
instanceMirrorKlass

在这里插入图片描述
Test_1_A
静态变量str的值存放在StringTable中,镜像类中存放的是字符串的指针
在这里插入图片描述
Test_1_B
str是类Test_1_A的静态属性,可以看到不会存储子类Test_1_B的镜像类中。
在这里插入图片描述
可以猜得到,通过子类Test_1_B访问父类Test_1_A的静态字段有两种实现方式:

  • 1.先去Test_1_B的镜像类中去取,如果有直接返回;如果没有,会沿着继承链将请求上跑。很明显,这种算法的性能随继承链的depth而上升,算法复杂度为O(n).
  • 2.借助另外的数据结构实现,使用K-V的格式存储,查询性能为O(1)
    Hotspot就是使用的第二种方式,借助另外的数据结构ConstantPoolCache,常量池类ConstantPool中有个属性_cache指向了这个结构。每一条数据对应一个类ConstantPoolCacheEntry.
    ConstantPoolCacheEntry在哪儿?在CosntantPoolCache对象后面,代码位置/openjdk/hotspot/src/share/vm/oops/cpCache.hpp,代码如图所示。这个公式的意思是ConstantPoolCache对象的地址加上ConstantPoolCache对象的内存大小
    在这里插入图片描述
    ConstantPoolCache
    常量池缓存是为常量池预留的运行时数据结构。保证所有字段访问和调用字节码的解释器运行时信息。缓存是在类被积极使用之前创建和初始化的。每个缓存项在解析时被填充。从图中的代码可以看出,是直接去获取ConstantPoolCacheEntry

类解析的过程ClassFileParser.cpp中的parseClassFile方法

在这里插入图片描述

1.魔数验证

在这里插入图片描述

2.版本号验证

在这里插入图片描述

3.解析常量池、访问权限等等

在这里插入图片描述

4.解析类的接口信息、方法信息、属性信息、父类信息

在这里插入图片描述

5.创建位于方法区的InstanceKlass对象

在这里插入图片描述

6.创建位于堆中的InstanceMirrorClass对象

在这里插入图片描述

疑惑

为什么HotSpot将BootstrapClassLoader使用C++语言编写而ExtClassLoader和AppClassLoader用java语言编写

HotSpot JVM将BootstrapClassLoader用C++语言比那些,而ExtClassLoader和AppClassLoader用Java语言编写,主要是处于以下几个原因:
1.引导类加载器(BootstrapClassLoader)
核心系统组件:BootstrapClassLoader是JVM的核心部分,负责加载Java核心库(如java.lang.*、java.util.*等)。这些核心库在JVM启动时必须
被加载,并且它们的加载过程必须在任何Java代码执行之前完成。
依赖关系:由于BootstrapClassLoader加载的是最基础的Java类库,它不能已离开于任何Java嘞,否则,会陷入加载循环(依赖的类还未加载,需要先加载依赖类)
性能和控制:使用C++编写可以更直接地控制内存和资源,提高性能和启动速度
2.扩展类加载器(ExtClassLoader)和应用类加载器(AppClassLoader)
实现简便:ExtClassLoader和AppClassLoader主要是加载扩展库和应用程序嘞,它们不需要像BootstrapClassLoader那样处理JVM启动的核心部分。因此可以用
Java语言编写,利用java自身的特性,使得代码更简洁和易维护
继承和扩展:用Java编写可以更方便地利用Java的面向对象特性,继承和扩展ClassLoader嘞,从而更容易地实现自定义类加载器。
运行时环境:在JVM启动之后,ExtClassLoader和AppClassLoader运行已经初始化的Java环境中,不需要担心加载器本身的类是否已经加载

总结:
BootStrapClassLoader使用C++编写,确保JVM在启动时能够加载最核心的类库,并且不依赖于任何Java类,避免循环依赖,同事提高性能和控制性
ExtClassLoader和AppClassLoader使用Java语言编写,方便实现和扩展,同时在JVM启动后利用现有的Java环境和面向对象特性,更易于维护和扩展。

这种设计确保了JVM的启动和运行时类加载机制既高效又灵活,能够满足不同阶段的需求

Tomcat为什么要打破双亲委派机制

在这里插入图片描述

我们知道,Java默认的类加载机制是通过双亲委派模型来实现的,而Tomcat实现的方式又和双亲委派模型有所区别。原因在于一个Tomcat容器允许同时运行多个Web程序,每个Web程序依赖的类又必须是相互隔离的。因此,如果Tomcat使用双亲委派模式来加载类的话,将导致Web程序依赖的类变为共享的。
举个例子,假如我们有两个Web程序,一个依赖A库的1.0版本,另一个依赖A库的2.0版本,它们都使用了类xxx,其实现的逻辑因类库版本的不同而结构完全不同。那么这两个Web程序的其中一个必然因为加载的Class不是所使用的Class而出现问题!而这对于开发来说是非常致命的

完整的的Tomcat类加载图。我们在这张图中看到很多类加载器,除了JDK自带的类加载器,我们尤其关心Tomcat自身持有的类加载器。仔细一点我们很容易发现:Catalina类加载器和Shared类加载器,它们并不是父子关系。为啥这样设计,我们得分析一下每个类加载的用途,才能知晓。
1.Common类加载器,负责加载Tomcat和Web应用都服用哦个的累
1.1 Catalina类加载器,负责加载Tomcat专用的类,而这些被加载的类在Web应用中将不可见
1.2 Shared类加载器,负责加载Tomcat下所有的Web应用程序都复用的类,而这些被加载的类在Tomcat中将不可见
1.2.1 WebApp类加载器,负责加载具体的某个Web应用程序所使用到的类,而这些被加载的类在Tomcat和其他的Web应用程序都将不可见(每一个Web应用程序对应一个WebApp类加载器)
1.2.2 Jsp类加载器,每个jsp页面一个类加载器,不同的jsp页面有不同的类加载器,方便实现jsp页面的热拔插

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

coffee_babe

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

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

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

打赏作者

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

抵扣说明:

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

余额充值