性能调优(一)JVM的类加载机制
一、jvm的类加载机制过程
1.类加载机制的概述

2.类加载机制的详细过程




注意:JVM采用懒加载机制,程序启动时仅加载包含main方法的主类及其直接依赖的类,其余类(静态内部类、工具类等)只有在首次被主动使用时才会加载和初始化。
二、双亲委派机制
1.双亲委派机制原理

以Math类加载过程为例:
① 应用程序类加载器加载Math类时,先检查它已经加载过的类里面有没有Math类,如果有就直接返回;如果没有,它不会自己去加载,而是委派扩展类加载器去加载。
② 同样扩展类加载器也是先检查它已经加载过的类里面有没有Math类,如果有就直接返回;如果没有,它也不会自己去加载,而是委托引导类加载器去加载。
③ 引导类加载器先检查它已经加载的类里面有没有Math类,如果有直接返回;如果没有,它会尝试去它对应的加载路径中加载,如果找不到就不会加载,那么它就委托扩展类加载器去加载。
④ 扩展类加载器也去尝试加载,如果它也在它对应的加载路径中也找不到该类就不加载,那么它就委托应用程序类加载器去加载。
⑤应用程序类加载器就去它对应的加载路径中加载,最终加载成功。
注意:
类加载过程主要是通过类加载器来实现的,而java里有几种类加载器:
引导类加载器:负责加载支撑JVM运行的位于jdk/jre/lib目录下的核心类库
扩展类加载器:负责加载支撑JVM运行的位于jdk/jre/lib/ext扩展目录中的JAR 类包
应用程序类加载器:负责加载自己写的类(负责加载的目录位于ClassPath路径下的类包,即自己的项目中的类)
自定义加载器:负责加载用户自定义路径的类包
代码实现部分:
双亲委派机制的实现核心是由loadClass()方法来实现的。
接下来是需要确定几点关系:
① 应用程序类加载器(AppClassLoader)的loadClass()的核心实现最终是调用ClassLoader.loadClass()方法来实现的。
首先是应用程序类加载器(AppClassLoader),它的继承关系如下:
\
在应用程序类加载器的loadClass()方法是调用它的父类URLClassLoader.loadClass()方法

而URLClassLoader.loadClass()方法又是调用它的父类loadClass()方法的,因为它的父类SecureClassLoader没有loadClass()方法,调用了ClassLoader.loadClass()方法,也就是说AppClassLoader的loadClass()方法最终是由ClassLoaDER.loadClass()方法来实现的


② 扩展类加载器(ExtClassLoader)没有loadClass()方法,因为它继承了它的父类的URLClassLoader.loadClass()方法,最终也是调用ClassLoader.loadClass()方法来实现的。

③ ClassLoader类中的Parent变量是扩展类加载器(ExtClassLoader)

代码实现:

2.loadClass方法在类加载过程
在类加载时,双亲委派机制通过类加载器在指定的加载路径中找到该类,并开始加载的具体过程如下:

而实现类加载过程主要是ClassLoader类的loadClass()方法进行类加载,而类中加载的核心是loadClass()方法中调用findClass方法来实现这一过程的。在类加载的过程有如下几步:
加载->>验证->>准备->>解析->>初始化
加载:在加载路径中找到指定的class文件,在磁盘中读取指定的class文件,并将class文件丢到java虚拟机内存中去
验证:验证字节码格式是否正确。比如:防止出现编译好的字节码被篡改
准备:将一些静态变量做一个默认值赋值。在类加载的过程中,静态变量是先赋默认值的。比如:代码中是public static int intData=23;那么在这一阶段就是给变量initData赋值为0,此时initData=0。同理String类型的变量赋值为null,boolean类型的变量赋值为false。
解析:java虚拟机将常量池内的符号引用替换为直接引用的过程。常量池内的符号引用指的是存储在磁盘上的.class文件在编译期生成的二进制资源表,跟内存没有关系,用来存储字面量和符号引用的,用于JVM加载时解析为直接引用。直接引用指的是直接指向目标在内存中的地址。可以理解为将存储在.class文件中的符号描述转换为在jvm内存中的内存地址。
初始化:对类的静态变量赋值为用户指定的值,同时这一阶段静态代码块也会执行的。比如:代码public static int
age=20;在"准备"这一阶段将age赋为默认值0,即age=0,在初始化这一阶段将age=20。
3.为什么要双亲委派机制
a.沙箱安全机制:比如自己写了一个java.lang.String.class类不会被加载,这样可以防止核心API库被随意篡改
b.避免类的重复加载:当父加载器已经加载了该类时,就没有必要子加载器再加载一次,保证了被加载的类的唯一性。
三、自定义类加载器
自定义类加载器只需要继承java.lang.ClassLoader类,该类有两个核心方法,一个是loadClass(String,boolean)实现双亲委派机制,另一个方法是findClass方法,默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法。
public class MyClassLoader extends ClassLoader { private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } //将磁盘上指定的字节码文件读取转换为二进制 private byte[] getClassBytes(String className) throws Exception{ className = className.replaceAll("\\.","/"); FileInputStream fs = new FileInputStream(classPath+"/"+className+".class"); int len=fs.available(); byte[] data=new byte[len]; fs.read(data); fs.close(); return data; } //重写ClassLoader类中的findClass方法 protected Class<?> findClass(String className) throws ClassNotFoundException { try{ byte[] data = getClassBytes(className); return defineClass(className,data,0,data.length); } catch(Exception e){ e.printStackTrace(); throw new ClassNotFoundException(className); } } } ===================调用
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException {
MyClassLoader myClassLoader = new MyClassLoader("D:/test");
Class clazz=myClassLoader.loadClass("com.blackboy.test2.User");
System.out.println(clazz.getClassLoader().getClass().getName());
}
四、打破双亲委派机制

public class MyClassLoader extends ClassLoader { private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } //将磁盘上指定的字节码文件读取转换为二进制 private byte[] getClassBytes(String className) throws Exception{ className = className.replaceAll("\\.","/"); FileInputStream fs = new FileInputStream(classPath+"/"+className+".class"); int len=fs.available(); byte[] data=new byte[len]; fs.read(data); fs.close(); return data; } //重写ClassLoader类中的findClass方法 protected Class<?> findClass(String className) throws ClassNotFoundException { try{ byte[] data = getClassBytes(className); return defineClass(className,data,0,data.length); } catch(Exception e){ e.printStackTrace(); throw new ClassNotFoundException(className); } } //重写父类的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) { long t0 = System.nanoTime(); long t1 = System.nanoTime(); //核心部分:如果不是自定义类加载器要加载的类,那么就有系统原来的双亲委派机制进行加载; //如果 if (!name.startsWith("com.blackboy.test2")){ c=this.getParent().loadClass(name); }else{ 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; } } }

浙公网安备 33010602011771号