类加载

在Java代码中,类型的加载、连接与初始化过程都是在程序运行期间完成的。

类型可以是Class,Interface, 枚举等。

Java虚拟机与程序的生命周期

在如下几种情况下,Java虚拟机将结束生命周期

1)执行了System.exit() 方法

2)程序正常执行结束

3)程序在执行过程中遇到了异常或者错误而异常终止。

4) 由于操作系统出现错误导致Java虚拟机进程终止。

1.JVM运行流程,JVM基本结构

2、类加载器双亲委派模型

3、ClassLoader源码解析

4、从源码分析实现自定义类加载器

一、JVM运行流程,JVM基本结构

JVM基本结构

类加载器,运行时数据区,执行引擎,本地接口

Class Files -> ClassLoader -> 运行时数据区 -> 执行引擎 -> 本地方法库

类的装载:

加载,连接(验证,准备,解析),初始化,使用,卸载

初始化: 执行类的构造器<clinit>,为类的静态变量赋初始值

构造器:

  1、static变量

2、staitc{} 语句

构造方法: 实例化的对象

二、类加载器双亲委派模型

1、为什么使用双亲委派模型

避免重复加载

2、JDK已有的类加载器

BootStrap ClassLoader JVM自己的类加载器,启动加载器。(C++) 主要加载rt.jar

Extension ClassLoader extends ClassLoader 扩展类加载器  加载%Java_home%lib/ext/*.jar

APP ClassLoader extends ClassLoader 应用加载器  -> Classpath

打印Class Loader

打印parent ClassLoader

null为启动类加载器。

三、ClassLoader源码解析

ClassLoader所在的路径

1、创建关于ClassLoader的Demo

public class MyTest15 {
public static void main(String[] args) {
String[] strings = new String[2];
System.out.println(strings.getClass());
System.out.println(strings.getClass().getClassLoader()); //启动类加载器
System.out.println("--------------------"); MyTest15[] myTest15s = new MyTest15[2];
System.out.println(myTest15s.getClass().getClassLoader()); //AppClassLoader System.out.println("--------------------"); int[] ints = new int[2];
System.out.println(ints.getClass().getClassLoader()); //原生类型没有classLoader }
}

  打印结果:

class [Ljava.lang.String;
null
--------------------
sun.misc.Launcher$AppClassLoader@18b4aac2
--------------------
null

  

2、定位到loadClass方法

loadClass(name,false)方法

    protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 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 {
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 = 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;
}
}

 3、双亲委派模型

如果parent不为空,则调用parent的loadClass方法。

findClass类的目的是自定义的ClassLoader

四、自定义类加载器 extends ClassLoader  -> 完成自定义加载路径

1) 创建Demo.java文件,路径为D:/tmp/Demo.java

然后编译成class文件

javac Demo.java

2) 创建自定义类加载器

package com.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream; public class MyClassLoader extends ClassLoader { private String path; //加载的路径
private String name; //类加载器名称 public MyClassLoader(String name, String path){
super(); //让系统类加载器成为该类的父加载器
this.name = name;
this.path = path;
} public MyClassLoader(ClassLoader parent, String name, String path){
super(parent); //显示指定父加载器
this.name = name;
this.path = path;
} /**
* 加载自定义的类,通过自定义的ClassLoader
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = readClassFileToByteArray(name);
return this.defineClass(name, data,0, data.length);
} /**
* 获取.class文件的字节数组
* com.classLoader.Demo ->
* D:/temp/com/classLoader/Demo.class
* @return
*/
private byte[] readClassFileToByteArray(String name) {
InputStream is = null;
byte[] returnData = null; name = name.replaceAll("\\.","/");
String filePath = this.path + name + ".class";
File file = new File(filePath); ByteArrayOutputStream os = new ByteArrayOutputStream();
try{
is = new FileInputStream(file);
int tmp = 0;
while ((tmp = is.read()) != -1){
os.write(tmp);
}
returnData = os.toByteArray();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if(is != null){
is.close();
} if(os != null){
os.close();
} }catch (Exception e2){ }
} return returnData; } @Override
public String toString() {
return this.name;
}
}

  

 

 

3、使用自定义类加载器

public class TestDemo {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
MyClassLoader loader = new MyClassLoader("MyClassLoadName1", "D:/tmp/");
Class<?> c = loader.loadClass("Demo");
c.newInstance();
}
}

  运行结果

Demo, MyClassLoadName1

4、测试父加载器

1)、工程里的Demo.java增加测试方法

2)、修改D盘下的Demo.java

增加包名

package com.classloader;

public class Demo {

    public Demo(){
System.out.println("Demo, " + this.getClass().getClassLoader());
} }

  路径为,并且重新生成class文件

3) 测试文件

public class TestDemo {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
MyClassLoader loader = new MyClassLoader("MyClassLoadName1", "D:/tmp/");
Class<?> c = loader.loadClass("com.classloader.Demo");
c.newInstance();
}
}

  运行结果:

说明使用的是父类加载器,加载的是工程里的那个Demo.class文件。

修改如下:

传入null,说明父加载器为启动加载器。

显示结果:

Demo, MyClassLoadNameChild

JVM 类加载器ClassLoader源码学习笔记的更多相关文章

  1. 第五章 类加载器ClassLoader源码解析

    说明:了解ClassLoader前,先了解 第四章 类加载机制 1.ClassLoader作用 类加载流程的"加载"阶段是由类加载器完成的. 2.类加载器结构 结构:Bootstr ...

  2. 类加载器ClassLoader源码解析

    1.ClassLoader作用 类加载流程的"加载"阶段是由类加载器完成的. 2.类加载器结构 结构:BootstrapClassLoader(祖父)-->ExtClassL ...

  3. JUC源码学习笔记4——原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法

    JUC源码学习笔记4--原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法 volatile的原理和内存屏障参考<Java并发编程的艺术> 原子类源码基于JDK8 ...

  4. Spring 源码学习笔记10——Spring AOP

    Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...

  5. Qt Creator 源码学习笔记04,多插件实现原理分析

    阅读本文大概需要 8 分钟 插件听上去很高大上,实际上就是一个个动态库,动态库在不同平台下后缀名不一样,比如在 Windows下以.dll结尾,Linux 下以.so结尾 开发插件其实就是开发一个动态 ...

  6. async-validator 源码学习笔记(五):Schema

    系列文章: 1.async-validator 源码学习(一):文档翻译 2.async-validator 源码学习笔记(二):目录结构 3.async-validator 源码学习笔记(三):ru ...

  7. Spring 源码学习笔记11——Spring事务

    Spring 源码学习笔记11--Spring事务 Spring事务是基于Spring Aop的扩展 AOP的知识参见<Spring 源码学习笔记10--Spring AOP> 图片参考了 ...

  8. Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点

    Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...

  9. Underscore.js 源码学习笔记(下)

    上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...

随机推荐

  1. 【JUC】8.CopyOnWriteArrayList源码分析

    CopyOnWriteArrayList 解决脏读问题:牺牲写的效率,提高读的效率 CopyOnWriteArrayList是一种读写分离的思想体现的ArrayList: 它将读写的操作对象分离开来: ...

  2. golang基础归纳

    1. hello-world package main import "fmt" func main(){ fmt.Println("Hello world, Go Go ...

  3. openstack创建实例时aborted: Block Device Mapping is Invalid

    问题产生原因: 直接先不创建新卷,点击否,待实例创建完毕后再分配卷.

  4. Java精通并发-锁粗化与锁消除技术实例演示与分析

    在上一次https://www.cnblogs.com/webor2006/p/11446473.html中对锁的升级进行了一个比较详细的理论化的学习,先回忆一下: 编译器对于锁的优化措施: 锁消除技 ...

  5. test20190830 NOIP 模拟赛

    100+70+0=170.这套题早就被上传到BZOJ上了,可惜我一到都没做过. BZOJ4765 普通计算姬 小G的计算姬可以解决这么个问题:给定一棵n个节点的带权树,节点编号为1到n,以root为根 ...

  6. 语义(Semantics)

    流计算语义(Semantics)的定义 每一条记录被流计算系统处理了几次 有三种语义: 1.At most once 一条记录要么被处理一次,要么没有被处理 2.At least once 一条记录可 ...

  7. strtol函数的用法——字符串转长整形

    /* strtol example */ #include <stdio.h> /* printf */ #include <stdlib.h> /* strtol */ in ...

  8. oracel: 通过特殊表序列来实现oracle自增id (mybatis实现自增id)

    本项目结合mybaits来使用. 加入有一个用户表:sys_user, 自增id为user_id. 怎么来实现id的自增呢? 通过sql语句select * from user_sequences,检 ...

  9. java书籍推荐[转]

    作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从.下面就按照学习顺序,给大家推荐下面这些JAVA书籍.一.Java编程入门类,选择大于努力,入门太重要.对 ...

  10. HTML5新增input标签属性

    一. input type属性 <form action=""> 邮箱<input type="email" name="" ...