1、JVM介绍
1.1、JVM是什么
Java Virtual Machine(Java虚拟机)是java程序实现跨平台的⼀个重要的工具(部件)。
HotSpot VM,相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机。
只要装有JVM的平台,都可以运行java程序。那么Java程序在JVM上是怎么被运行的?
通过介绍以下JVM的三个组成部分,就可以了解到JVM内部的⼯作机制
- 类加载系统:负责完成类的加载
- 运行时数据区:在运行Java程序的时候会产生的各种数据会保存在运行时数据区
- 执行引擎:执行具体的指令(代码)
1.2、学习的目的
我们学JVM相关知识的目的是为了充分理解jvm内部的工作流程,来掌握如何通过相应的参数配置,实现JVM的调优。
2、类加载系统
2.1、类的加载过程
⼀个类被加载进JVM中要经历哪几个过程
- 加载: 通过io流的方式把字节码文件读入到jvm中(方法区)
- 校验:通过校验字节码文件的头8位的16进制是否是java魔数cafebabe
- 准备:为类中的静态部分开辟空间并赋初始化值
- 解析:将符号引用转换成直接引用。——静态链接
- 初始化:为类中的静态部分赋指定值并执⾏静态代码块。
类被加载后,类中的类型信息、方法信息、属性信息、运行时常量池、类加载器的引用等信息会被加载到元空间中。
2.2、类加载器
类是谁来负载加载的?——类加载器
-
Bootstrap ClassLoader 启动类加载器:负载加载jre/lib下的核心类库中的类,⽐如rt.jar、charsets.jar
-
ExtClassLoader 扩展类加载器:负载加载jre/lib下的ext⽬录内的类
ext 加载路径:System.getProperty("java.ext.dirs");
- AppClassLoader 应⽤类加载器:负载加载⽤户⾃⼰写的类
app 加载路径:System.getProperty("java.class.path");
- ⾃定义类加载器:⾃⼰定义的类加载器,可以打破双亲委派机制。
3、双亲委派机制
3.1、双亲委派机制介绍
当类加载器进⾏加载类的时候,类的加载需要向上委托给上⼀级的类加载器,上⼀级继续向上委托,直到启动类加载器。启动类加载器去核心类库中找,如果没有该类则向下委派,由下⼀级扩展类加载器去扩展类库中,如果也没有继续向下委派,直到找不到为止,则报类找不到的异常。
public class TestJMM {
//静态属性
public static int baseData = 10;
//静态属性
public static Student student = new Student();
public static String hello = "hello";
}
应⽤类加载器怎么加载Student和String呢?需要通过双亲委派机制
3.2、为什么要有双亲委派机制
- 防⽌核心类库中的类被随意篡改
- 防⽌类的重复加载
3.3、双亲委派机制核心源码
ClassLoader.class
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.4、全盘委托机制
当⼀个类被当前的ClassLoader加载时,该类中的其他类也会被当前该ClassLoader加载。除非指明其他由其他类加载器加载。
3.5、自定义类加载器实现双亲委派机制
- 自定义类加载机制
package com.qf.jvm;
import java.io.FileInputStream;
import java.lang.String;
/**
* @author Thor
* @公众号 Java架构栈
*/
public class MyClassLoader extends ClassLoader{
private String classPath;
public MyClassLoader(String classPath){
this.classPath = classPath;
}
@Override
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
try {
//1.读入指定路径的classPath下的类
String path = name.replace('.', '/').concat(".class");
FileInputStream fileInputStream = new FileInputStream(classPath+"/"+path);
byte[] data = new byte[fileInputStream.available()];
fileInputStream.read(data);
fileInputStream.close();
//2.加载该类
return defineClass(name, data,0,data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
}
- 启动自定义类加载器
package com.qf.jvm;
import java.lang.reflect.Method;
import java.lang.String;
/**
* @author Thor
* @公众号 Java架构栈
*/
public class TestMyClassLoader {
public static void main(String[] args) throws Exception {
//1.创建自定义类加载器,指定加载路径
MyClassLoader myClassLoader = new MyClassLoader("C:\\note\\java\\JVM-2021-千锋\\文档及代码\\my_class");
//2.指定要加载的类名
Class<?> clazz = myClassLoader.loadClass("com.qf.jvm.JVMAnalyze");
//3.反射创建对象
Object obj = clazz.newInstance();
//4.创建add方法对象
Method add = clazz.getDeclaredMethod("add", null);
//5.调用对象的add方法
Object result = add.invoke(obj);
//6.打印结果
System.out.println(result);
}
}
3.6、自定义类加载器打破双亲委派机制
/**
重写loadClass⽅法
*/
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) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//对于Object类,使用父加载器
if(!name.startsWith("com.qf.jvm")){
c = this.getParent().loadClass(name);
}else{
c = findClass(name);
}
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
本文章参考B站 千锋教育JVM全套教程(含jvm调优、jvm虚拟机、jvm面试题、jvm源码详解)系统玩转java虚拟机全程干货无废话,仅供个人学习使用,部分内容为本人自己见解,与千锋教育无关。