玩命学JVM(二)—类加载机制
前言
Java程序运行图:
上一篇玩命学JVM(一)—认识JVM和字节码文件我们简单认识了 JVM 和字节码文件。那JVM是如何使用字节码文件的呢?从上图清晰地可以看到,JVM 通过类加载器完成了这一过程。
以下是类加载机制的知识框架:
接下来我们对思维导图中重难点部分做补充。
1. 是什么?
类的加载就是将 .class 文件的二进制数据读入到内存中,将其放在 JVM 的运行时数据区的方法区内。然后在堆区内创建一个 java.lang.Class 对象,用于封装类在方法区内的数据结构。
5. 双亲委派模型
双亲委派模型图如下:
对于“双亲委派模型”,首先需要纠正一点,“双亲”并不是说它有“两个亲”。实际上行“双亲委派模型”和“双”毫无关系,只和“亲”有关系。
其实“双亲”是翻译的一个错误,原文出处是“parent”,被翻译成了“双亲”,在计算机领域更常见的说法是“父节点”。所以如果将“双亲委派模型”改为“父委派模型”,应该更好理解。
结合实际的类加载器来说,就是:
- 每个类加载器都会向上找自己父类加载器尝试完成类加载;
- 父加载器加载失败会向下找加载器尝试加载。
如图:
接下来我们从源码上来分析下 双亲委派模型
除Bootstrap ClassLoader
外,其它的类加载器都是ClassLoader
的子类。加载类的方法为loadClass
,查看源码可发现,loadClass
在ClassLoader
中有具体的实现,且在各个子类中都没有被覆盖。
先介绍三个重要的函数,对后续的源码阅读有帮助:
loadClass
:调用父类加载器的loadClass,加载失败则调用自己的findClass方法。
findClass
:根据名称读取文件存入字节数组。
defineClass
:把一个字节数组转为Class对象。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
// 在JVM中查看类是否已经被加载
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
// 调用父类加载器的 loadClass方法,parent是该类加载器的父类,parent的值可能为 Application ClassLoader、Extension ClassLoader,当想要继续往上找 Extension ClassLoader时,由于Bootstrap ClassLoader是C/C++实现的,所以在java中是Null
c = parent.loadClass(name, false);
} else {
// 寻找 Bootstrap ClassLoader 加载
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();
// 父加载器开始尝试加载.class文件,加载成功就返回一个java.lang.Class,加载不成功就抛出一个ClassNotFoundException,给子加载器去加载
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) {
// 如果要解析这个.class文件的话,就解析一下,解析的作用主要就是将符号引用替换为直接引用的过程
resolveClass(c);
}
return c;
}
}
所谓的双亲委派模型,就是利用了loadClass
只在父类中实现了这一点。
自定义类加载器
自定义类加载主要有两种方式:
遵守双亲委派模型:继承ClassLoader,重写findClass()方法。
破坏双亲委派模型:继承ClassLoader,重写loadClass()方法。 通常我们推荐采用第一种方法自定义类加载器,最大程度上的遵守双亲委派模型。
我们看一下实现步骤
(1)创建一个类继承ClassLoader抽象类
(2)重写findClass()方法
(3)在findClass()方法中调用defineClass()
第一步,自定义一个实体类Person.java,我把它编译后的Person.class放在D盘根目录下:
package com.xrq.classloader;
public class Person
{
private String name;
public Person()
{
}
public Person(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String toString()
{
return "I am a person, my name is " + name;
}
}
第二步,自定义一个类加载器,里面主要是一些IO和NIO的内容,另外注意一下 defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class----只要二进制字节流的内容符合Class文件规 范。我们自定义的MyClassLoader继承自java.lang.ClassLoader,就像上面说的,只实现findClass方法:
public class MyClassLoader extends ClassLoader
{
public MyClassLoader()
{
}
public MyClassLoader(ClassLoader parent)
{
super(parent);
}
protected Class<?> findClass(String name) throws ClassNotFoundException
{
File file = getClassFile(name);
try
{
byte[] bytes = getClassBytes(file);
Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
return c;
}
catch (Exception e)
{
e.printStackTrace();
}
return super.findClass(name);
}
private File getClassFile(String name)
{
File file = new File("D:/Person.class");
return file;
}
private byte[] getClassBytes(File file) throws Exception
{
// 这里要读入.class的字节,因此要使用字节流
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
WritableByteChannel wbc = Channels.newChannel(baos);
ByteBuffer by = ByteBuffer.allocate(1024);
while (true)
{
int i = fc.read(by);
if (i == 0 || i == -1)
break;
by.flip();
wbc.write(by);
by.clear();
}
fis.close();
return baos.toByteArray();
}
}
第三步,Class.forName有一个三个参数的重载方法,可以指定类加载器,平时我们使用的Class.forName("XX.XX.XXX")都是使用的系统类加载器Application ClassLoader。写一个测试类:
public class TestMyClassLoader
{
public static void main(String[] args) throws Exception
{
MyClassLoader mcl = new MyClassLoader();
Class<?> c1 = Class.forName("com.xrq.classloader.Person", true, mcl);
Object obj = c1.newInstance();
System.out.println(obj);
System.out.println(obj.getClass().getClassLoader());
}
}
运行结果:
I am a person, my name is null
com.xrq.classloader.MyClassLoader@5d888759
参考文献
https://baijiahao.baidu.com/s?id=1636309817155065432&wfr=spider&for=pc
https://blog.csdn.net/qq_44836294/article/details/105439753
玩命学JVM(二)—类加载机制的更多相关文章
- 一文教你读懂JVM的类加载机制
Java运行程序又被称为WORA(Write Once Run Anywhere,在任何地方运行只需写入一次),意味着我们程序员小哥哥可以在任何一个系统上开发Java程序,但是却可以在所有系统上畅通运 ...
- JVM内存结构 JVM的类加载机制
JVM内存结构: 1.java虚拟机栈:存放的是对象的引用(指针)和局部变量 2.程序计数器:每个线程都有一个程序计数器,跟踪代码运行到哪个位置了 3.堆:对象.数组 4.方法区:字节流(字节码文件) ...
- JVM之类加载机制
JVM之类加载机制 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 类加载五部分 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这 ...
- JVM的类加载机制全面解析
什么是类加载机制 JVM把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被JVM直接使用的Java类型,这就是JVM的类加载机制. 如果你对Class文件的结 ...
- 大白话谈JVM的类加载机制
前言 我们很多小伙伴平时都是做JAVA开发的,那么作为一名合格的工程师,你是否有仔细的思考过JVM的运行原理呢. 如果懂得了JVM的运行原理和内存模型,像是一些JVM调优.垃圾回收机制等等的问题我们才 ...
- JVM活学活用——类加载机制
类的实例化过程 有父类的情况 1. 加载父类静态 1.1 为静态属性分配存储空间并赋初始值 1.2 执行静态初始化块和静态初始化语句(从上至下) 2. 加载子类静态 2.1 为静态 ...
- JVM学习——类加载机制(学习过程)
JVM--类加载机制 2020年02月07日14:49:19-开始学习JVM(Class Loader) 类加载机制 类加载器深入解析与阶段分解 在Java代码中,类型的加载.连接与初始化过程中都是在 ...
- JVM虚拟机—JVM的类加载机制
1 什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构 ...
- JVM的类加载机制
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类加载的过程: 包括加载.链接(含验证.准备 ...
随机推荐
- chromevue扩展 vue-devtools-master 谷歌vue的扩展程序
1,在百度网盘中下载压缩包,网盘地址:https://pan.baidu.com/s/1BnwWHANHNyJzG3Krpy7S8A ,密码:xm6s 2,将压缩包解压到F盘,F:\chromeVue ...
- Android开发之强大的网络判断工具,判断是否联网,判断是wifi还是3g网络等java工具代码类
作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985, 转载请说明出处. 给大家分享一个Android开发者常用的工具类.主要针对网络判断的 功能强大.下面 ...
- Android开发之获取APP的应用程序名称以及版本名称信息java工具类
//跟App相关的辅助类 public class AppUtils { private AppUtils() { /* cannot be instantiated */ throw new Uns ...
- 使用Android的硬件缩放技术优化执行效率
Unity3D研究院之使用Android的硬件缩放技术优化执行效率 http://www.xuanyusong.com/archives/3205 Android中GLSurfaceView在横竖屏切 ...
- 用 Spring Boot 和 MybatisPlus 快速构建项目
自动生成 public class MPGenerator { public static void main(String[] args) { AutoGenerator autoGenerator ...
- vmware-workstation迁移虚拟机 15pro到12版本
最近将测试的几台虚拟机进行了迁移,有几个点要注意,分享一下 1.环境介绍: 源服务器-ip-172.16.96.x 目标服务器-ip-172.16.96.x VMware版本-VMwareworkst ...
- Java进阶专题(十三) 从电商系统角度研究多线程(上)
前言 本章节主要分享下,多线程并发在电商系统下的应用.主要从以下几个方面深入:线程相关的基础理论和工具.多线程程序下的性能调优和电商场景下多线程的使用. 多线程J·U·C 线程池 概念 回顾线程创 ...
- 详解 Python 的二元算术运算,为什么说减法只是语法糖?
原题 | Unravelling binary arithmetic operations in Python 作者 | Brett Cannon 译者 | 豌豆花下猫("Python猫&q ...
- Java实现内嵌浏览器
创建项目 ----> 导入需要的jar ----> 代码实现 需要的jar: https://pan.baidu.com/s/1MEZ1S0LnKSMGQm24QWgmCw 代码: ...
- flag在index里
题目:http://123.206.87.240:8005/post/ 我们来看这一题 首先打开题目 他让点击就点击呗 跳转到另一个新的界面 这就没了??? ... 没思路就对了 //假装证明自 ...