【jvm】01-双亲委派都会说,破坏双亲委派你会吗

欢迎关注b站账号/公众号【六边形战士夏宁】,一个要把各项指标拉满的男人。该文章已在github目录收录。

屏幕前的大帅比大漂亮如果有帮助到你的话请顺手点个赞、加个收藏这对我真的很重要。别下次一定了,都不关注上哪下次一定。

1.类的生命周期

首先可以从图中明确类的生命周期

1)遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,如果类型没有进行过初始 化,则需要先触发其初始化阶段。能够生成这四条指令的典型Java代码场景有: ·使用new关键字实例化对象的时候。 ·读取或设置一个类型的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外) 的时候。·调用一个类型的静态方法的时候。

2)使用java.lang.reflect包的方法对类型进行反射调用的时候,如果类型没有进行过初始化,则需 要先触发其初始化。

3)当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先 初始化这个主类。

5)当使用JDK 7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解 析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句 柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。

6)当一个接口中定义了JDK 8新加入的默认方法(被default关键字修饰的接口方法)时,如果有 这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

  1. 验证

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

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

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

  1. 加载

1)文件格式验证

2)元数据验证

3)字节码验证

4)符号引用验证

  1. 准备

    为静态变量分配内存并设置类变量初始值(null)

    注意这里有个概念为方法区,是逻辑概念

    jdk8以前使用永久代实现方法区(有专门的永久代区域),在后面使用了元空间实现方法区,但实例变量物理存储地址其实是放到了堆中。所以说静态变量、字符串等存在堆中也对,存在方法区也对。

  2. 解析

    解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这7

    类符号引用进行,分别对应于常量池的CONSTANT_Class_info、CON-STANT_Fieldref_info、

    CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、

    CONSTANT_MethodType_info、CONSTANT_MethodHandle_info、CONSTANT_Dyna-mic_info和 CONSTANT_InvokeDynamic_info 8种常量类型。

    主要有以下4种解析过程。

    1)类或接口的解析

    2)字段解析

    3)方法解析

    4)接口方法解析

  3. 初始化

  4. 使用

  5. 卸载

2.jdk8双亲委派模型

双亲委派核心代码package sun.misc.Launcher;

public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
int var3 = var1.lastIndexOf(46);
if (var3 != -1) {
SecurityManager var4 = System.getSecurityManager();
if (var4 != null) {
var4.checkPackageAccess(var1.substring(0, var3));
}
} if (this.ucp.knownToNotExist(var1)) {
Class var5 = this.findLoadedClass(var1);
if (var5 != null) {
if (var2) {
this.resolveClass(var5);
} return var5;
} else {
throw new ClassNotFoundException(var1);
}
} else {
return super.loadClass(var1, var2);
}
}
// 演示的时候漏了一段核心代码 先是一层一层往上抛,如果父加载器找不到则自己加载
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 检查当前类加载器是否已经加载了该类
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) { //如果当前加载器父加载器不为空则委托父加载器加载该类
c = parent.loadClass(name, false);
} else { //如果当前加载器父加载器为空则委托引导类加载器加载该类
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();
//都会调用URLClassLoader的findClass方法在加载器的类路径里查找并加载该类
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;
}
}

从代码和图中可知所谓双亲委派就是一层一层往上找,每层加载的代码不同避免了重复加载,通常用户所写的代码在系统类加载器

演示代码

package com.example.demo.lesson.jvm.loader;

import sun.misc.Launcher;

import java.net.URL;

public class ClassLoaderExe {
public static void main(String[] args) {
// 核心rt.jar中的类加载器 是C++加载的,因此这里为null
System.out.println(String.class.getClassLoader());
// 扩展包的加载器 ExtClassLoader
System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader());
// 应用加载器 AppClassLoader
System.out.println(ClassLoaderExe.class.getClassLoader()); System.out.println(""); // 获取系统ClassLoader
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
// appClassLoader的父加载器
ClassLoader extClassLoader = appClassLoader.getParent();
// extClassLoader的父加载器
ClassLoader boostrapClassLoader = extClassLoader.getParent(); System.out.println("the bootstrapLoader : " + boostrapClassLoader);
System.out.println("the extClassloader : " + extClassLoader);
System.out.println("the appClassLoader : "+ appClassLoader); System.out.println("==============bootstrapLoader加载的文件===================="); URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urLs.length; i++) {
System.out.println(urLs[i]);
}
System.out.println(""); System.out.println("==============extClassloader加载的文件====================");
System.out.println(System.getProperty("java.ext.dirs")); System.out.println(""); System.out.println("==============appClassLoader 加载的文件====================");
System.out.println(System.getProperty("java.class.path"));
}
}

3.jdk9破坏双亲委派模型

核心代码在jdk11进行了迁移jdk.internal.loader.ClassLoaders

	private static final BootClassLoader BOOT_LOADER;
private static final PlatformClassLoader PLATFORM_LOADER;
private static final AppClassLoader APP_LOADER; // Creates the built-in class loaders.
static {
// -Xbootclasspath/a or -javaagent with Boot-Class-Path attribute
String append = VM.getSavedProperty("jdk.boot.class.path.append");
BOOT_LOADER =
new BootClassLoader((append != null && !append.isEmpty())
? new URLClassPath(append, true)
: null);
PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER); // A class path is required when no initial module is specified.
// In this case the class path defaults to "", meaning the current
// working directory. When an initial module is specified, on the
// contrary, we drop this historic interpretation of the empty
// string and instead treat it as unspecified.
String cp = System.getProperty("java.class.path");
if (cp == null || cp.isEmpty()) {
String initialModuleName = System.getProperty("jdk.module.main");
cp = (initialModuleName == null) ? "" : null;
}
URLClassPath ucp = new URLClassPath(cp, false);
APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
}

从图和代码中可知在java模块化后通过判断该模块由哪个类加载器加载就直接加载不用再一层层往上找

演示代码

package com.example.demo.lesson.jvm.loader;

import java.net.URL;

public class ClassLoaderPlant {
public static void main(String[] args) {
System.out.println(String.class.getClassLoader());
System.out.println(ClassLoaderPlant.class.getClassLoader()); System.out.println(""); // 获取系统ClassLoader
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
// appClassLoader的父加载器
ClassLoader platformClassLoader = appClassLoader.getParent();
// platformClassLoader的父加载器
ClassLoader boostrapClassLoader = platformClassLoader.getParent(); System.out.println("the bootstrapLoader : " + boostrapClassLoader);
System.out.println("the extClassLoader : "+ platformClassLoader);
System.out.println("the appClassLoader : "+ appClassLoader);
}
}

参考资料

《深入理解Java虚拟机》-周志明

# 【jvm】01-双亲委派都会说,破坏双亲委派你会吗的更多相关文章

  1. Tomcat类加载器破坏双亲委派

    转载:https://blog.csdn.net/qq_38182963/article/details/78660779 http://www.cnblogs.com/aspirant/p/8991 ...

  2. 【JVM】浅谈双亲委派和破坏双亲委派

    一.前言 笔者曾经阅读过周志明的<深入理解Java虚拟机>这本书,阅读完后自以为对jvm有了一定的了解,然而当真正碰到问题的时候,才发现自己读的有多粗糙,也体会到只有实践才能加深理解,正应 ...

  3. JDBC、Tomcat为什么要破坏双亲委派模型?

    问题一:双亲委派模型是什么 如果一个类加载器收到了加载某个类的请求,则该类加载器并不会去加载该类,而是把这个请求委派给父类加载器,每一个层次的类加载器都是如此,因此所有的类加载请求最终都会传送到顶端的 ...

  4. 破坏双亲委托机制的一些情况---Tomcat和JDBC,破坏后的安全问题

    采用双亲委托机制的原因 类加载器就是将字节码搬进jvm方法区的组件.我们知道,JVM识别加载进来的类是通过类加载器+类全名完成的,也就是说同一个类由不同类加载器加载进去的话就会被视为不同的类.jdk提 ...

  5. jdbc 加载数据库驱动如何破坏双亲委托模式

    导读      通过jdbc链接数据库,是每个学习Java web 方向的人必然一开始会写的代码,虽然现在各路框架都帮大家封装好了jdbc,但是研究一下jdbc链接的套路还是很意义     术语以及相 ...

  6. ClassLoader类加载器 & Java类加载机制 & 破坏双亲委托机制

    ClassLoader类加载器 Java 中的类加载器大致可以分成两类: 一类是系统提供的: 引导类加载器(Bootstrap classloader):它用来加载 Java 的核心库(如rt.jar ...

  7. JAVA基础|从Class.forName初始化数据库到SPI破坏双亲委托机制

    代码托管在:https://github.com/fabe2ry/classloaderDemo 初始化数据库 如果你写过操作数据库的程序的话,可能会注意,有的代码会在程序的开头,有Class.for ...

  8. 深入理解JVM(一)类加载器部分:双亲委派模型

    类加载器的父亲委托机制 在父亲委托机制中,各个类加载器按照父子关系形成了树形结构,除了根类加载器之外,其余的类加载器都有且只有一个父加载器. 先让最顶层可以加在的父加载器加栽(所有可加载的加载器中,处 ...

  9. JVM—01

    目录 1.1 JVM系统架构图 2.1 类加载器 2.1.1 双亲委派机制 2.1.2 沙箱安全机制 3.1 Native 4.1 PC寄存器 1.1 JVM系统架构图 JVM是什么? JVM是Jav ...

随机推荐

  1. android 获取uri的正确文件路径的办法

    private String getRealPath( Uri fileUrl ) { String fileName = null; if( fileUrl != null ) { if( file ...

  2. fastjson转换数字时,格式化小数点

    使用fastjson类库转换java对象时,对于BigDecimal类型,有时需要特殊格式,比如: 1.0,转为json时候,要求显式为1,因此需要在转换时做处理.步骤如下: 1.新建类,实现Valu ...

  3. How exactly does Google AdWords work?

    The key to how Google AdWords works is the Quality Score. Quality Score is generally how well an ad ...

  4. 集合类——Map集合、Properties属性文件操作

    1.Map集合 Collection集合的特点是每次进行单个对象的保存,若要对一对对象来进行保存就只能用Map集合来保存.即Map集合中一次可以保存两个对象,且这两个对象的关系是key = value ...

  5. Linux学习 - IP地址配置

    1 首先选择桥接模式 2 配置IP.子网掩码.网关.DNS setup 本例中使用的是无线网连接, IP地址:  192.168.3.195 子网掩码:  255.255.255.0 网关: 192. ...

  6. OC-copy,单例

    总结 编号 主题 内容 一 NSFileManager NSFileManager介绍/用法(常见的判断)/文件访问/文件操作 二 集合对象的内存管理 集合对象的内存管理/内存管理总结 三 *copy ...

  7. springmvc中如何自定义类型转换器

    package com.hope.utils;import org.springframework.core.convert.converter.Converter;import org.spring ...

  8. 访问Github速度很慢以及解决方法(系统通用)

    原因分析1,CDN,Content Distribute Network,可以直译成内容分发网络,CDN解决的是如何将数据快速可靠从源站传递到用户的问题.用户获取数据时,不需要直接从源站获取,通过CD ...

  9. 『学了就忘』Linux服务管理 — 76、RPM包安装的服务管理

    目录 1.独立服务的启动管理 2.独立服务的自启动管理 方式一: 方式二:(推荐) 方式三: 3.验证 1.独立服务的启动管理 (1)使用/etc/init.d/目录中的启动脚本启动服务(推荐) [r ...

  10. ios获取文件MD5值

    一般我们在使用http或者socket上传或者下载文件的时候,经常会在完成之后经行一次MD5值得校验(尤其是在断点续传的时候用的更 多),校验MD5值是为了防止在传输的过程当中丢包或者数据包被篡改,在 ...