别再乱看教程了!从源码剖析JVM类加载机制,打通双亲委派机制!

简介: 别再乱看教程了!从源码剖析JVM类加载机制,打通双亲委派机制!

万丈高楼平地起。大家入门JAVA必然绕不开JVM的研究,在保证基础知识储备的同时,把面试的火箭顺手造了岂不是美滋滋。

aa6e5f07d689e1325c73475d0f79f837.gif


干货概要

  • 搞通类加载器,自己动手做类加载器!
  • 为啥有双亲委派机制?
  • 如何打破双亲委派机制及其应用?


1 搞通类加载器

方便理解晦涩原理,先来一波简单实例代码开开胃。

package TaorenCoding
public class Math {
  public static final int initData = 666;
  public static User user = new User();
 public int compute() { //一个方法对应一块栈帧内存区域
   int a = 1;
   int b = 2;
   int c = (a + b) * 10;
   return c;
 }
 public static void main(String[] args) {
   Math math = new Math();
   math.compute();
 } }


如上代码,看的懂吧,够清晰吧。好了那接下来就是真正的硬菜。

584a8d6c5a783be724118639693ba05c.png

扒拉出其中的核心要点:类加载过程

其中loadClass的类加载过程有如下几步:

加载 : 在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

验证 : 校验字节码文件的正确性(如魔数等二进制内容)

准备 :给类的静态变量分配内存,并赋予默认值

解析 :将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用

初始化: 对类的静态变量初始化为指定的值,执行静态代码块

使用

卸载


自定义类加载器

自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是 loadClass(String, boolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法。


直接继承方法开造!

1 public class MyClassLoaderTest {
2   static class MyClassLoader extends ClassLoader {
3   private String classPath;
4
5   public MyClassLoader(String classPath) {
6     this.classPath = classPath;
7   }
8
9   private byte[] loadByte(String name) throws Exception {
10     name = name.replaceAll("\\.", "/");
11     FileInputStream fis = new FileInputStream(classPath + "/" + name+ ".class");
13     int len = fis.available();
14     byte[] data = new byte[len];
15     fis.read(data);
16     fis.close();
17     return data;
18 }
19
20 protected Class<?> findClass(String name) throws ClassNotFoundException {
21     try {
22       byte[] data = loadByte(name);
23     //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节
数组。
24     return defineClass(name, data, 0, data.length);
25     } catch (Exception e) {
26       e.printStackTrace();
27       throw new ClassNotFoundException();
28   }
29   }
31 }
32
33 public static void main(String args[]) throws Exception {
34     //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定      //义类加载器的父加载器设置为应用程序类加载器AppClassLoader
35   MyClassLoader classLoader = new MyClassLoader("D:/test");
36 //D盘创建 test/com/tuling/jvm 几级目录,将User类的复制类User1.class丢入该目录
37   Class clazz = classLoader.loadClass("com.tuling.jvm.User1");
38   Object obj = clazz.newInstance();
39   Method method = clazz.getDeclaredMethod("sout", null);
40   method.invoke(obj, null);
41   System.out.println(clazz.getClassLoader().getClass().getName());
42   }
43 }
**运行结果:**
 =======自己的加载器加载类调用方法=======
 com.taoren.jvm.MyClassLoaderTest$MyClassLoader


双亲委派机制

为啥要使用双亲委派机制?

  • 沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改
  • 避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性


打破双亲委派机制

Tomcat作为一个经典的web容器,我们经常用来部署多个实例。有没有想过,我们所部署的多个应用时不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离,由此,Tomcat是打破双亲委派机制的典型应用。主要原因如下:


部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程序,那么要有10份相同的类库加载进虚拟机。

web容器也有自己依赖的类库,不能与应用程序的类库混淆。基于安全考虑,应该让容器的类库和程序的类库隔离开来。

web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中运行,但程序运行后修改jsp已经是司空见惯的事情, web容器需要支持 jsp 修改后不用重启。

a3ea9858b6949d5e6aebd2cbda874cbd.gif

相关文章
|
2月前
|
存储 Java 程序员
【JVM】——JVM运行机制、类加载机制、内存划分
JVM运行机制,堆栈,程序计数器,元数据区,JVM加载机制,双亲委派模型
|
4月前
|
缓存 前端开发 Java
JVM知识体系学习二:ClassLoader 类加载器、类加载器层次、类过载过程之双亲委派机制、类加载范围、自定义类加载器、编译器、懒加载模式、打破双亲委派机制
这篇文章详细介绍了JVM中ClassLoader的工作原理,包括类加载器的层次结构、双亲委派机制、类加载过程、自定义类加载器的实现,以及如何打破双亲委派机制来实现热部署等功能。
169 3
|
4月前
|
存储 Java C语言
【JVM】类加载机制
【JVM】类加载机制
35 1
|
5月前
|
Arthas Java 测试技术
JVM —— 类加载器的分类,双亲委派机制
类加载器的分类,双亲委派机制:启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器;JDK8及之前的版本,JDK9之后的版本;什么是双亲委派模型,双亲委派模型的作用,如何打破双亲委派机制
JVM —— 类加载器的分类,双亲委派机制
|
6月前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
110 0
|
6月前
|
开发者 C# Windows
WPF布局大揭秘:掌握布局技巧,轻松创建响应式用户界面,让你的应用程序更上一层楼!
【8月更文挑战第31天】在现代软件开发中,响应式用户界面至关重要。WPF(Windows Presentation Foundation)作为.NET框架的一部分,提供了丰富的布局控件和机制,便于创建可自动调整的UI。本文介绍WPF布局的基础概念与实现方法,包括`StackPanel`、`DockPanel`、`Grid`等控件的使用,并通过示例代码展示如何构建响应式布局。了解这些技巧有助于开发者优化用户体验,适应不同设备和屏幕尺寸。
193 0
|
6月前
|
安全 前端开发 Java
【JVM的秘密揭秘】深入理解类加载器与双亲委派机制的奥秘!
【8月更文挑战第25天】在Java技术栈中,深入理解JVM类加载机制及其双亲委派模型是至关重要的。JVM类加载器作为运行时系统的关键组件,负责将字节码文件加载至内存并转换为可执行的数据结构。其采用层级结构,包括引导、扩展、应用及用户自定义类加载器,通过双亲委派机制协同工作,确保Java核心库的安全性与稳定性。本文通过解析类加载器的分类、双亲委派机制原理及示例代码,帮助读者全面掌握这一核心概念,为开发更安全高效的Java应用程序奠定基础。
104 0
|
6月前
|
安全 前端开发 Java
【JVM 探秘】ClassLoader 类加载器:揭秘 Java 类加载机制背后的秘密武器!
【8月更文挑战第25天】本文全面介绍了Java虚拟机(JVM)中的类加载器,它是JVM的核心组件之一,负责将Java类加载到运行环境中。文章首先概述了类加载器的基本工作原理及其遵循的双亲委派模型,确保了核心类库的安全与稳定。接着详细阐述了启动、扩展和应用三种主要类加载器的层次结构。并通过一个自定义类加载器的例子展示了如何从特定目录加载类。此外,还介绍了类加载器的完整生命周期,包括加载、链接和初始化三个阶段。最后强调了类加载器在版本隔离、安全性和灵活性方面的重要作用。深入理解类加载器对于掌握JVM内部机制至关重要。
213 0
|
6月前
|
存储 监控 算法
深入解析JVM内部结构及GC机制的实战应用
深入解析JVM内部结构及GC机制的实战应用
|
6月前
|
监控 JavaScript Java
JVM源码级别分析G1发生FullGC元凶的是什么
线上系统遭遇频繁Old GC问题,监控显示出现多次“to-space exhausted”日志,这表明垃圾回收过程中因年轻代 Survivor 区或老年代空间不足导致对象晋升失败。通过 JVM 源码分析,此问题源于对象转移至老年代失败时,JVM 无法找到足够的空间存放存活对象。进一步排查发现大对象分配占用了预留空间,加剧了空间不足的情况。使用 JFR 分析工具定位到定期报表序列化导致大量大对象生成,通过改用堆外内存进行序列化输出,最终解决了频繁 Old GC 问题。
195 0