首页 > 其他分享 >面试类-JVM原理(三)

面试类-JVM原理(三)

时间:2024-01-30 09:22:56浏览次数:20  
标签:委派 Java java ClassLoader 面试 双亲 JVM 原理 加载

42.能说一下类的生命周期吗?

一个类从被加载到虚拟机内存中开始,到从内存中卸载,整个生命周期需要经过七个阶段:加载 (Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化 (Initialization)、使用(Using)和卸载(Unloading),其中验证、准备、解析三个部分统称为连接(Linking)。

类的生命周期
              类的生命周期

43.类加载的过程知道吗?

加载是 JVM 加载的起点,具体什么时候开始加载,《Java 虚拟机规范》中并没有进行强制约束,可以交给虚拟机的具体实现来自由把握。

在加载过程,JVM 要做三件事情:

加载
        加载
  • 1)通过一个类的全限定名来获取定义此类的二进制字节流。

  • 2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

  • 3)在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。

加载阶段结束后,Java 虚拟机外部的二进制字节流就按照虚拟机所设定的格式存储在方法区之中了,方法区中的数据存储格式完全由虚拟机实现自行定义,《Java 虚拟机规范》未规定此区域的具体数据结构。

类型数据妥善安置在方法区之后,会在 Java 堆内存中实例化一个 java.lang.Class 类的对象, 这个对象将作为程序访问方法区中的类型数据的外部接口。

44.类加载器有哪些?

主要有四种类加载器:

  • 启动类加载器(Bootstrap ClassLoader)用来加载 java 核心类库,无法被 java 程序直接引用。

  • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

  • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

  • 用户自定义类加载器 (user class loader),用户通过继承 java.lang.ClassLoader 类的方式自行实现的类加载器。

45.什么是双亲委派机制?

双亲委派模型
                双亲委派模型

双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去完成加载。

46.为什么要用双亲委派机制?

答案是为了保证应用程序的稳定有序。

例如类 java.lang.Object,它存放在 rt.jar 之中,通过双亲委派机制,保证最终都是委派给处于模型最顶端的启动类加载器进行加载,保证 Object 的一致。反之,都由各个类加载器自行去加载的话,如果用户自己也编写了一个名为 java.lang.Object 的类,并放在程序的 ClassPath 中,那系统中就会出现多个不同的 Object 类。

47.如何破坏双亲委派机制?

如果不想打破双亲委派模型,就重写 ClassLoader 类中的 fifindClass()方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。而如果想打破双亲委派模型则需要重写 loadClass()方法。

48.历史上有哪几次双亲委派机制的破坏?

双亲委派机制在历史上主要有三次破坏:

双亲委派模型的三次破坏
                        双亲委派模型的三次破坏

第一次破坏

双亲委派模型的第一次“被破坏”其实发生在双亲委派模型出现之前——即 JDK 1.2 面世以前的“远古”时代。

由于双亲委派模型在 JDK 1.2 之后才被引入,但是类加载器的概念和抽象类 java.lang.ClassLoader 则在 Java 的第一个版本中就已经存在,为了向下兼容旧代码,所以无法以技术手段避免 loadClass()被子类覆盖的可能性,只能在 JDK 1.2 之后的 java.lang.ClassLoader 中添加一个新的 protected 方法 findClass(),并引导用户编写的类加载逻辑时尽可能去重写这个方法,而不是在 loadClass()中编写代码。

第二次破坏

双亲委派模型的第二次“被破坏”是由这个模型自身的缺陷导致的,如果有基础类型又要调用回用户的代码,那该怎么办呢?

例如我们比较熟悉的 JDBC:

各个厂商各有不同的 JDBC 的实现,Java 在核心包\lib里定义了对应的 SPI,那么这个就毫无疑问由启动类加载器加载器加载。

但是各个厂商的实现,是没办法放在核心包里的,只能放在classpath里,只能被应用类加载器加载。那么,问题来了,启动类加载器它就加载不到厂商提供的 SPI 服务代码。

为了解决这个问题,引入了一个不太优雅的设计:线程上下文类加载器 (Thread Context ClassLoader)。这个类加载器可以通过 java.lang.Thread 类的 setContext-ClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。

JNDI 服务使用这个线程上下文类加载器去加载所需的 SPI 服务代码,这是一种父类加载器去请求子类加载器完成类加载的行为。

第三次破坏

双亲委派模型的第三次“被破坏”是由于用户对程序动态性的追求而导致的,例如代码热替换(Hot Swap)、模块热部署(Hot Deployment)等。

OSGi 实现模块化热部署的关键是它自定义的类加载器机制的实现,每一个程序模块(OSGi 中称为 Bundle)都有一个自己的类加载器,当需要更换一个 Bundle 时,就把 Bundle 连同类加载器一起换掉以实现代码的热替换。在 OSGi 环境下,类加载器不再双亲委派模型推荐的树状结构,而是进一步发展为更加复杂的网状结构。

49.你觉得应该怎么实现一个热部署功能?

我们已经知道了 Java 类的加载过程。一个 Java 类文件到虚拟机里的对象,要经过如下过程:首先通过 Java 编译器,将 Java 文件编译成 class 字节码,类加载器读取 class 字节码,再将类转化为实例,对实例 newInstance 就可以生成对象。

类加载器 ClassLoader 功能,也就是将 class 字节码转换到类的实例。在 Java 应用中,所有的实例都是由类加载器,加载而来。

一般在系统中,类的加载都是由系统自带的类加载器完成,而且对于同一个全限定名的 java 类(如 com.csiar.soc.HelloWorld),只能被加载一次,而且无法被卸载。

这个时候问题就来了,如果我们希望将 java 类卸载,并且替换更新版本的 java 类,该怎么做呢?

既然在类加载器中,Java 类只能被加载一次,并且无法卸载。那么我们是不是可以直接把 Java 类加载器干掉呢?答案是可以的,我们可以自定义类加载器,并重写 ClassLoader 的 findClass 方法。

想要实现热部署可以分以下三个步骤:

  • 1)销毁原来的自定义 ClassLoader
  • 2)更新 class 类文件
  • 3)创建新的 ClassLoader 去加载更新后的 class 类文件。

到此,一个热部署的功能就这样实现了。

50.Tomcat 的类加载机制了解吗?

Tomcat 是主流的 Java Web 服务器之一,为了实现一些特殊的功能需求,自定义了一些类加载器。

Tomcat 类加载器如下:

Tomcat类加载器
                Tomcat类加载器

Tomcat 实际上也是破坏了双亲委派模型的。

Tomact 是 web 容器,可能需要部署多个应用程序。不同的应用程序可能会依赖同一个第三方类库的不同版本,但是不同版本的类库中某一个类的全路径名可能是一样的。如多个应用都要依赖 hollis.jar,但是 A 应用需要依赖 1.0.0 版本,但是 B 应用需要依赖 1.0.1 版本。这两个版本中都有一个类是 com.hollis.Test.class。如果采用默认的双亲委派类加载机制,那么无法加载多个相同的类。

所以,Tomcat 破坏了双亲委派原则,提供隔离的机制,为每个 web 容器单独提供一个 WebAppClassLoader 加载器。每一个 WebAppClassLoader 负责加载本身的目录下的 class 文件,加载不到时再交 CommonClassLoader 加载,这和双亲委派刚好相反。

标签:委派,Java,java,ClassLoader,面试,双亲,JVM,原理,加载
From: https://www.cnblogs.com/pxzbky/p/17995758

相关文章

  • 面试类-JVM优化(二)
    32.有哪些常用的命令行性能监控和故障处理工具?操作系统工具top:显示系统整体资源使用情况vmstat:监控内存和CPUiostat:监控IO使用netstat:监控网络使用JDK性能监控工具jps:虚拟机进程查看jstat:虚拟机运行时信息查看jinfo:虚拟机配置查看jmap:内存映像(导出)jhat:堆转......
  • 【面试突击】计算机网络面试实战(下)
    欢迎关注公众号【11来了】,及时收到AI前沿项目工具及新技术的推送!在我后台回复「资料」可领取编程高频电子书!在我后台回复「面试」可领取硬核面试笔记!Https的工作原理Http的内容是明文传输的,铭文数据经过中间代理服务器、路由器、wifi热点等多个物理节点,如果被劫持会导致传输......
  • Kotlin扩展函数原理解析
    一、扩展函数扩展函数可以方便地给现有类增加属性和方法而不改动类地代码。二、原理funString.addTo(s:String):String{returnthis+s}反编译:@Metadata(mv={1,6,0},k=2,d1={"\u0000\n\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0002\u001a\u......
  • 隐写术-数字水印的原理、实现及应用
    导语前段时间有一则阿里员工外泄信息被捕获的报道。大致内容是阿里的某位员工,在内部办公软件截图,使用PS工具修掉截图上的可见水印,然后传播出去,但阿里通过图片携带的不可见水印,解读了截图员工的员工编码,从而找到了泄漏图片的员工。一时间,图片的盲水印技术受到了广泛关注。本文针对......
  • MySQL InnoDB事务原理
    事务事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。 特性原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。一致性(Consistency):事务完成时,必......
  • JVM(Java虚拟机) 整理
    JVM整体结构本文主要说的是HotSpot虚拟机,JVM全称是JavaVirtualMachine,中文译名:Java虚拟机简化一下:Java字节码文件Class文件本质上是一个以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件中,JVM根据其特定的规则解析该二进制数据,从而得到......
  • 面试官:说一下零拷贝技术的实现原理?
    零拷贝(Zero-copy)技术是一种计算机操作系统中用于提高数据传输效率的优化策略。在传统的数据传输过程中,需要将数据从一个缓冲区拷贝到另一个缓冲区,然后再传输给目标。这涉及到多次的CPU和内存之间的数据拷贝操作,会消耗CPU的时间和内存带宽。而零拷贝技术通过直接共享数据的内......
  • JVM对象创建与内存分配
    对象创建流程对象内存分配对象内存分配流程图对象栈上分配我们通过JVM内存分配可以知道JAVA中的对象都是在堆上进行分配,当对象没有被引用的时候,需要依靠GC进行回收内存,如果对象数量较多的时候,会给GC带来较大压力,也间接影响了应用的性能。为了减少临时对象在堆内分配的数量,JV......
  • Spring整合jasypt原理分析
    前言在我们系统中,有很多敏感数据,如MySQL及Redis的账号密码信息等,jasypt可以帮我们加密这些信息,使系统更加的安全。使用添加maven依赖<dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-boot-starter</artifactId><version>2......
  • 每日一道面试题:Java中序列化与反序列化
    写在开头哈喽大家好,在高铁上码字的感觉是真不爽啊,小桌板又拥挤,旁边的小朋友也比较的吵闹,影响思绪,但这丝毫不影响咱学习的劲头!哈哈哈,在这喧哗的车厢中,思考着这样的一个问题,Java中的对象是如何在各个方法,或者网络中流转的呢?通过这个问题便引出了我们今天的主人公:序列化与反序列化!......