十一.吊打面试官系列-JVM优化-深入JVM类加载机制

前言

从本篇文章开始我们来探讨JVM相关的知识,内容附带JVM的启动,JVM内存模型,JVM垃圾回收机制,JVM参数调优等,跟着文章一步一步走相信你对JVM会有一个不一样的认识,如果觉得文章对你有所帮助请给个好评吧。

JVM类加载系统

1.JVM的启动流程

当我们编写好Java源代码之后如hello.java ,通过javac命令把hello.java文件编译为hello.class文件,然后通过java.exe去执行hello.class 字节码文件,这个时候Java会启动JVM虚拟机,虚拟机是通过jvm.ddl文件创建的,底层是由C++实现的,具体的启动流程如下
在这里插入图片描述
对上图做一个步骤解释:

  1. 编译源代码:使用Java编译器(javac)将Hello.java编译成字节码文件(Hello.class)。这一步将源代码转换成JVM能够理解的指令集。
  2. 启动JVM:通过命令行界面调用java.exe来启动Java虚拟机(底层是由C++来实现的),java.exe是JVM的入口点,负责加载和运行Java应用程序。
  3. 创建启动类加载器:JVM在启动时,会首先加载BootstrapClassLoader启动类加载器(这个类加载器也是c++实现的),它是Java类加载器体系的最顶层加载器,负责加载核心类库。
  4. 创建启动器:启动类加载器会加载Launcher , C++会调用Java代码创建JVM启动器:sun.misc.Launcher ,该启动器的作用是用来加载器其他的类加载器,比如:AppClassLoader应用类加载器
  5. 加载应用类:Launcher 会根据当前类Hello.class找到其ClassLoader并加装它,也就是AppClassLoader应用类加载器,它负责加载用户自定义的Java类(如Hello类)
  6. 加载Class : 通过AppClassLoader 加载Hello.class字节码文件
  7. 执行Main方法:找到class类中的main方法,JVM通过调用类的main方法作为程序的入口点来执行Java程序(这一步也是c++调用的)
  8. 运行Java程序:Java程序开始执行,直到遇到main方法结束或者发生异常而终止。

2.类加载器

当我们在Java中编写代码并引用某个类时,这个类是如何被加载到JVM中的呢?这涉及到JVM的类加载器(ClassLoader)以及类加载的流程和双亲委派机制。下面我将详细解释这些概念。

JVM的类加载器是负责将类的字节码文件(通常是.class文件)加载到JVM中,并为其生成对应的Class对象的过程。类加载器是Java运行时环境的一部分,是Java程序获取字节码文件的重要途径。

在这里插入图片描述

JVM提供了三种主要的类加载器:

  • 引导类加载器(Bootstrap ClassLoader):这是JVM的内置类加载器,主要负责加载Java的核心类库,一般对应JAVA_HOME/lib 目录中的JAR包,如java.lang.*、java.util.*等。由于它并不是Java类库的一部分,而是JVM自身的实现,所以它并不继承自java.lang.ClassLoader。
  • 扩展类加载器(Extension ClassLoader):这是Java的标准扩展类加载器,负责加载Java的扩展类库,一般对应JAVA_HOME/lib/ext目录中的JAR包。它是java.lang.ClassLoader的子类,由sun.misc.Launcher$ExtClassLoader实现。
  • 应用类加载器:Application ClassLoader:也称为系统类加载器(ApplicationClassLoader)或默认类加载器(System ClassLoader),负责加载应用程序的类路径(classpath)下的所有类包括pom.xml导入的jar。它是java.lang.ClassLoader的子类,由sun.misc.Launcher$AppClassLoader实现。在Java应用程序中,我们通常使用的就是这个类加载器。

除了以上三种主要的类加载器,我们还可以自定义类加载器,通过继承java.lang.ClassLoader类并重写其相关方法来实现。

注意:这些类加载器并没有实际上的继承关系

3.双亲委派机制

JVM的双亲委派机制是Java语言服务器级别的安全策略,主要思想是在类加载过程中,子类委托给父类加载器优先加载,即:当一个类需要被加载时,它首先会委托给其父类加载器进行加载。如果父类加载器无法加载该类,那么子类加载器才会尝试自己加载,如果父类加载过了子类就不会再加载了。这种层层委派的方式确保了类加载的有序性和唯一性
在这里插入图片描述
双亲委派的好处在于:

  1. 类加载安全策略:例如我们自己编写一个 java.lang.String 是否会覆盖java自带的String类呢?答案是无法覆盖,因为BootStrapClassLoader优先加载了java.lang.String 后,AppClassLoader在加载我们自己的java.lang.String的时候会检查重复加载,也就不会再加载了。保证了类的唯一性
  2. 保证有序性 : JVM启动必须要加载一些基础的类,比如:Object.clas 这些基础的类会通过BootstrapClassLoader 和 ExtClassLoader 优先加载后,再加载我们自己的类,否则JVM无法启动,启动也会报错。

下面是AppClassLoader的类加载源码,可以看得出来JVM在加载类之前会查找类是否已经被加载,如果没加载就会调用 parent.loadClass 让父类优先加载,如果加载失败再调用findClass自己加载

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
        	//1.首先,检查类是否已加载
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                	//如果父类加载器不为空,则优先委派父类进行加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                    	//如果父类加载器为空,则查找 Bootstrap 类加载器,如果找不到则返回null
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
				
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //如果c == null 说明父类加载失败,则自己加载
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }


4.打破双亲委派

在JVM中,类加载器的双亲委派机制是一种默认的行为,它确保了类加载的有序性和安全性。然而,在某些特殊情况下,开发者可能需要打破这种机制来实现特定的功能,例如热部署、插件化开发等。以下是一些打破双亲委派机制的方法:

  1. 自定义类加载器

最直接的方式是创建自定义的类加载器,并在其加载类的过程中不遵循双亲委派机制。这可以通过在自定义类加载器的loadClass方法中直接加载类,而不是先调用父类加载器的loadClass方法来实现。

public class CustomClassLoader extends ClassLoader {  
    @Override  
    public Class<?> loadClass(String name) throws ClassNotFoundException {  
        // 自定义加载逻辑,不调用super.loadClass(name)  
        // ...  
    }  
}

然而,这样做可能会破坏Java的安全模型,因为它允许自定义类加载器加载Java核心库中的类,这可能会导致安全问题。

  1. 使用线程上下文类加载器

在Java中,每个线程都有一个与之关联的上下文类加载器(ContextClassLoader)。这个类加载器可以通过Thread.currentThread().getContextClassLoader()来获取。线程上下文类加载器为Java应用程序提供了一种在运行时动态加载类的方式,而不必受双亲委派机制的限制。

Thread.currentThread().setContextClassLoader(new CustomClassLoader());

然后,可以使用这个上下文类加载器来加载类,而不是使用默认的类加载器。

  1. 使用Java的代理类加载器

在某些情况下,可以使用Java的代理类加载器(如URLClassLoader)来加载类,这些类加载器提供了更多的灵活性来加载类。虽然它们通常遵循双亲委派机制,但可以在必要时被修改或扩展来打破这个机制。

初始之外还有其他的方式比如:使用Java 9的模块化系统(JPMS),但它提供了一种新的方式来管理类的加载和隔离。 ; 或者使用使用OSGi(Open Service Gateway initiative)它提供了自己的类加载器机制,允许不同的模块(bundle)独立地加载和管理类,这个一般我们接触的较少。

需要注意的是,打破双亲委派机制可能会导致类加载和安全性方面的问题。因此,在决定这样做之前,应该仔细考虑其潜在的影响,并确保采取了适当的措施来确保系统的稳定性和安全性。

我们熟知的Tomcat就打破了双亲委派机制,它通过自定义类加载器的方式来实现APP应用加载隔离具体的内容请看《深入源码剖析Tomcat如何打破双亲委派》

5.类加载流程

JVM(Java虚拟机)的类加载流程主要包括:加载,验证,准备,解析,初始化 几个阶段:
在这里插入图片描述
类的加载、验证、准备、解析和初始化这五个阶段通常被称为类的链接(Linking)过程。

  1. 加载(Loading):

    使用到某个类时,JVM会通过类的全限定名查找和加载class文件,并通过IO读入字节码文件,将其加载到JVM中,在方法区(元空间)会存储好class,而在堆内存中会生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

  2. 验证(Verification):

    确保被加载的类的正确性和安全性。包括文件格式验证、元数据验证、字节码验证和符号引用验证。

  3. 准备(Preparation):

    为类的静态变量分配内存,并将其初始化为默认值,比如 static int a = 1 , 在这里会赋初始值 0。这里不包括用final修饰的静态变量,因为final在编译的时候就会分配了,准备阶段会显式赋值

  4. 解析(Resolution):

    把类中的符号引用转换为直接引用。这主要是将类名、字段名、方法名等符号引用转换为指向方法区中的实际内存地址的直接引用,这个过程叫:静态链接,而动态链接是在程序运行期间完成的将符号引用替换为直接引用

  5. 初始化(Initialization) :

    为类的静态变量赋予正确的初始值,也就是第三步准备阶段的今天变量赋正确的初始值(如果有的话)。这个阶段会执行类构造器<clinit>()方法,这是由编译器自动收集类中的所有类变量的赋值动作和静态代码块(static块)中的语句合并产生的。

  6. 使用(Using):

    类的初始化完成后,就可以通过实例化类、调用类的静态方法等方式来使用这个类了。

  7. 卸载(Unloading):

    当类的生命周期结束时,JVM的垃圾回收机制会回收类的内存,这个过程称为类的卸载。但在Java中,类的卸载是由JVM来控制的,开发者通常不需要显式地卸载类。

在这里插入图片描述

文章对你有帮助请给个好评,下一章:JVM优化-深入JVM内存模型

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/630834.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring事务中的@Transactional注解中的参数说明

目录 注解声明 propagation &#xff08;事务传播行为&#xff09; REQUIRED 外部没有事务则创建当前事务 外部有事务则合并(加入)事务​编辑 REQUIRES_NEW 外部没有事务则创建一个事务 外部有事务则将外部事务挂起&#xff0c;内部影响外部&#xff0c;外部不影响…

终于搞懂Linux 设备树中的#address-cells,#size-cells 和reg 属性

目录 一、前置知识 1. 处理器平台2. reg 属性的基本格式3. reg 属性的作用 reg 用法 二、#address-cells 和 #size-cells 属性 1. 示例1 2. 示例23. 示例3 一、前置知识 要理解#address-cells和#size-cell 这两个属性&#xff0c;就要先了解 reg属性。 1. 处理器平台 下…

对比测评3款BI分析工具

前不久&#xff0c;一位准备入职阿里的学弟问我&#xff0c;他要做电商数据分析&#xff0c;电商有庞杂的标签、模型、数据和业务逻辑&#xff0c;菜鸟应该要具备什么样的分析能力啊&#xff1f; 我看了他的岗位职责&#xff0c;主要是负责经营决策支持、专题分析和数据看板搭…

深入剖析Tomcat(八) 载入器与打破双亲委派机制的自定义类加载器

写这篇文章让我头大了好几天&#xff0c;书中描述的内容倒是不多&#xff0c;可能也是那会Tomcat的现状。如今Tomcat发展了好多代&#xff0c;加上springboot的广泛应用&#xff0c;导致现在的类加载的步骤和Tomcat资料中描述的大相径庭。可能也是由于微服务的发展&#xff0c;…

903. 昂贵的聘礼[dijsktra堆优化版]

题目描述 年轻的探险家来到了一个印第安部落里。 在那里他和酋长的女儿相爱了&#xff0c;于是便向酋长去求亲。 酋长要他用 10000 个金币作为聘礼才答应把女儿嫁给他。 探险家拿不出这么多金币&#xff0c;便请求酋长降低要求。 酋长说&#xff1a;”嗯&#xff0c;如果你能够…

【C++】学习笔记——多态_1

文章目录 十二、继承8. 继承和组合 十三、多态1. 多态的概念2. 多态的定义和实现虚函数重写的两个特殊情况override 和 final 3. 多态的原理1. 虚函数表 未完待续 十二、继承 8. 继承和组合 我们已经知道了什么是继承&#xff0c;那组合又是什么&#xff1f;下面这种情况就是…

【开发】模型部署笔记

目录 模型量化 模型量化 1、模型量化优点 低精度模型表示模型权重数值格式为FP16&#xff08;半精度浮点&#xff09;或者INT8&#xff08;8位定点整数&#xff09;&#xff0c;但是目前低精度往往就指代INT8。常规精度模型则一般表示模型权重数值格式为FP32&#xff08;32位…

从RTTR谈Reflection机制

虽然C11引入了RTTI、Metaprogramming 等技术&#xff0c;但C在Reflection编程方面依旧功能有限。在社区上&#xff0c;RTTR则提供了一套C编写的反射库&#xff0c;补充了C在Reflection方面的缺陷。 零、环境 操作系统Windows 11Visual StudioVisual Studio Community 2022 CMa…

使用JasperReport工具,生成报表模版,及通过JavaBean传参,常见问题及建议

1.下载JasperReport工具 下载地址:社区版 - Jaspersoft 社区 邮箱:lorettepatri.ckoa5434gmail.com 密码:Zx123456. 2.工具使用方法注意 1.一次参数需要在左下角Parameters中新建,直接拖转右上角的TextField不会自动新建参数,到头来还是要在Parameters中新建 2.循环参数需…

实在智能AI+RPA:引领数字化转型的超自动化智能体

引言 在数字化时代&#xff0c;企业面临着前所未有的挑战和机遇。数字化转型不仅是企业生存的需要&#xff0c;更是实现持续增长和创新的关键。AIRPA作为数字化转型的重要驱动力&#xff0c;正帮助企业实现业务流程的自动化和智能化&#xff0c;从而提升效率、降低成本、增强竞…

做抖店的门槛高吗?一个月的时间能入门吗?基础问题解答如下

我是王路飞。 抖店&#xff0c;依旧是普通人做抖音最好的渠道&#xff0c;没有之一&#xff0c;依旧值得我们all in。 这是我对2024年抖音小店的看法和态度&#xff0c; 那么做抖店的门槛高吗&#xff1f;新手用一个月的时间能做到入门吗&#xff1f;投入和回报的数据是多少…

【MySQL数据库开发设计规范】之SQL使用规范

欢迎点开这篇文章&#xff0c;自我介绍一下哈&#xff0c;本人姑苏老陈 &#xff0c;是一名JAVA开发老兵。 本文收录于 《MySQL数据库开发设计规范》专栏中&#xff0c;该专栏主要分享一些关于MySQL数据库开发设计相关的技术规范文章&#xff0c;定期更新&#xff0c;欢迎关注&…

时间序列预测:探索性数据分析和特征工程的实用指南

时间序列分析是数据科学和机器学习领域最广泛的主题之一:无论是预测金融事件、能源消耗、产品销售还是股票市场趋势&#xff0c;这一领域一直是企业非常感兴趣的领域。 随着机器学习模型的不断进步&#xff0c;使除了传统的统计预测方法(如回归模型、ARIMA模型、指数平滑)外&a…

结合多模态 AI 谷歌展示 AR 眼镜原型机;Meta 被曝开发带摄像头的 AI 耳机丨 RTE 开发者日报 Vol.204

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」&#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

(实测验证)【移远EC800M-CN 】TCP 透传

引言 本文章使用自研“超小体积TTL转4GGPS集成模块”进行实测验证&#xff1b; 1、配置移远EC800M-CN TCP 透传 串口助手发送&#xff1a; ATQIOPEN1,0,"TCP","36.137.226.30",39755,0,2 //配置服务器地址和端口号&#xff1b; 4G模组返回…

C++(week3):数据结构与算法

文章目录 (十一) 常用数据结构1.动态数组(1)模型(2).h与.c(3)实现 2.链表(1)模型(2)分类(3)基本操作(API)(4)实现(5)链表常见面试题(6)空间与时间 3.栈(1)模型(2)基本操作(3)实现(4)栈的应用 4.队列(1)模型(2)基本操作(API)(3)实现(4)队列的应用 5.哈希表(1)哈希表的提出原因(2…

Qwen 开源标杆

Qwen的博客 在线体验Qwen开源版本、非常丝滑 不算量化、已经开源的Qwen1.5 版本有9个&#xff1a; 0.5B、1.8B、4B、7B、14B、32B、72B、110B、MoE-A2.7B 闭源已经发展到 Qwen-Max-0428、网页端从2.1升级到2.5 Qwen API详情 一些记录&#xff1a; 1、Qwen1.5 110B&#x…

echart树状图图表

根据后端返回的树状结构&#xff0c;渲染echart树状图&#xff0c;并且默认二级节点后是折叠的 父级引入组件 .tree_chart {width: 100%;height: calc(100% - 30px);}<div class"tree_chart"><EachartTree :treeData"allData.treeData" /><…

conan2 基础入门(02)-安装

conan2 基础入门(02)-安装 文章目录 conan2 基础入门(02)-安装⭐前言⭐安装python安装安装包安装自行操作 ⭐验证配置环境变量命令行验证conan配置文件 END ⭐前言 Conan 2.0: C and C Open Source Package Manager 官方提供三种安装conan的方式。分别为&#xff1a; Recommen…

哈希表的理解和实现

目录 1. 哈希的概念 (是什么) 2. 实现哈希的两种方式 (哈希函数) 2.1. 直接定址法 2.2. 除留余数法 2.2.1. 哈希冲突 3. 补充知识 3.1. 负载因子 3.2. 线性探测和二次探测 4. 闭散列实现哈希表 (开放定址法) 4.1. 开放定址法的实现框架 4.2. Xq::hash_table::insert…