和朋友交流总是觉得自己基础薄弱,以及去年出去面试被问到偏底层点的问题就很无奈。一直想沉下心来学习一下Java底层以及面试八股文,从今天开始,希望自己能够长期坚持下去,坚持卷。本文建议有一定基础的人看。码农都是很直接的,直接上干货
示例类:
package com.ruoyuan.test;
public class Math {
public int calc(){
int data1 = 1;
int data2 = 2;
return data1*data2;
}
public static void main(String[] args) {
Math math = new Math();
math.calc();
}
}
加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载
在磁盘上找到并通过IO读字节码文件,使用到类时才会加载。加载阶段在内存中生成一个代表该类的java.lang.Class的对象。用于方法区这个类各种数据的访问入口
根据Java虚拟机规范,来校验加载进来的“.class”文件中的内容,是否符合指定的规范
给类的静态变量分配内存,并且赋予初始值
虚拟机将常量池内的符号引用替换为直接引用的过程
赋值(对类的静态变量初始化为指定的值,执行静态代码块)类被加载到方法区中后主要包含 运行时常量池、类型信息、字段信息、方法信息、类加载器的 引用、对应class实例的引用等信息
对应class实例的引用:类加载器在加载类信息放到方法区中后,会创建一个对应的Class 类型的 对象实例放到堆(Heap)中, 作为开发人员访问方法区中类定义的入口和切入点
负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等
加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR 类包
负责加载ClassPath路径下的类包,主要就是加载自己写的那 些类
负责加载用户自定义路径下的类包
双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载。如果有感兴趣的可以去研究一下:AppClassLoader 的loadClass方法最终会调用其父类ClassLoader的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();
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;
}
}
自定义类加载器只需要继承java.lang.ClassLoader,该类的核心方法有一个loadClass(String, boolean)实现双亲委派机制,还有一个是findClass()默认实现是:空方法-如下:
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
所以自定义类加载器主要是手动实现:findClass方法
public class MyClassLoaderTest {
public static class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
private byte[] loadByte(String name) throws Exception {
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
//defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节 数组。
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
}
public static void main(String args[]) throws Exception {
//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载 器设置为应用程序类加载器AppClassLoader
MyClassLoader classLoader = new MyClassLoader("D:/practice_work");
//D盘创建 practice_work/com/ruoyuan/demo/main 几级目录,将MainTest1类的复制类MainTest1.class丢入该目录
Class clazz = classLoader.loadClass("com.ruoyuan.demo.main.MainTest1");
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("testFun", null);
method.invoke(obj, null);
System.out.println(clazz.getClassLoader().getClass().getName());
}
}
上文有说到loadClass(String, boolean)实现双亲委派机制,那么要打破该机制就得修改loadClass方法。
附上该方法的jdk源码:
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;
}
}
观察代码发现,其实要打破双亲委派机制,其实就是去掉加载父类代码。
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();
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;
}
public static void main(String args[]) throws Exception {
MyClassLoader classLoader = new MyClassLoader("D:/practice_work");
Class clazz = classLoader.loadClass("lang.String");
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("testFun", null);
method.invoke(obj, null);
System.out.println(clazz.getClassLoader().getClass().getName());
}
以上就是关于JVM类加载的部分知识。JVM包含知识点很多,请关注后续更新
如有问题,希望大家评论区留言多多指教!如觉得写得还可以,就给一个赞吧!