一、java命令執行代碼大致流程
1、windows調用C++程序實現java虛擬機;
2、創建一個引導類加載器(BootstrapClassloader);
3、引導類加載器創建JVM啓動器launcher,由lunacher類創建其他類加載器(擴展類加載器和應用類加載器)
二、類加載的大致過程
1、加載,根據path找到class文件;
2、驗證,校驗class內容是否符合規範;
3、準備,靜態變量賦默認值,常量直接復賦值;
4、解析,將符號引用轉換爲直接引用;符號引用可以理解爲“public static ”這一類的符號轉化爲內存中的地址;直接引用就是內存地址的引用;
(1)靜態連接:比如static一類的方法,在類加載期間就轉換爲直接引用了;
(2)動態連接:方法的調用,“dosomething()”,在調用時才轉換爲直接引用;
5、初始化,爲靜態變量賦值,執行靜態方法
三、類加載器和雙親委派機制
1、引導類加載器:加載rt.jar等核心模塊;
2、擴展類加載器:加載擴展包裏面的類;
3、應用程序類加載器:加載用戶自己變成的文件
4、自定義類加載器:加載用戶自定義路徑下的類
實例:
package com.company;
import java.net.URL;
import com.sun.crypto.provider.DESedeKeyFactory;
import sun.misc.Launcher;
/**
* @date 2020/7/2
*/
public class TestJdkClassLoader {
public static void main(String[] args) {
System.out.println(String.class.getClassLoader());
System.out.println(DESedeKeyFactory.class.getClassLoader().getClass().getName());
System.out.println(TestJdkClassLoader.class.getClassLoader().getClass().getName());
System.out.println();
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader extClassLoader = appClassLoader.getParent();
ClassLoader bootStrapLoader = extClassLoader.getParent();
System.out.println("the bootStrapLoader"+bootStrapLoader);
System.out.println("the extClassLoader"+extClassLoader);
System.out.println("the appClassLoader"+appClassLoader);
System.out.println();
System.out.println("bootStrapLoader 加載以下文件");
URL[] urls = Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i]);
}
System.out.println();
System.out.println("extClassLoader 加載以下文件");
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println();
System.out.println("appclassloader 加載以下文件");
System.out.println(System.getProperty("java.class.path"));
}
}
雙親委派的過程:
簡單說一下大致過程:
首先去自定義類加載中查找有沒有加載過的類,沒有就一直向上委派;直到引導類加載也沒找到;那麼引導類加載器開始嘗試加載,如果成功加載則返回;否則向下嘗試;一直到自定義類加載類嘗試加載。
源碼只要在java.lang.ClassLoader#loadClass(java.lang.String, boolean)這個方法中:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 先查找一下
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;
}
}
爲什麼使用雙親委派機制?
1、安全
jvm的核心類庫只能由引導類加載器加載;避免了安全問題;
2、效率
一些共同使用的基礎jar包,只會由指定加載器加載一次,然後就都能使用了,避免了重複加載。
自定義類加載器
繼承ClassLoader類,重寫findclass方法;
package com.ysy.JVM類加載;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author shanyangyang
* @date 2020/7/2
*/
public class MyClassLoaderTest {
static class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
@Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
return super.loadClass(name, resolve);
}
@Override protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
//該方法將一個字節數組轉換爲Class對象,這個字節數組就是最終讀取的字節數組
return defineClass(name, data, 0, data.length);
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
private byte[] loadByte(String name) throws IOException {
name.replace("\\.", "/");
FileInputStream fileInputStream = new FileInputStream(classPath + "/" + name + ".class");
int len = fileInputStream.available();
byte[] bytes = new byte[len];
fileInputStream.read(bytes);
fileInputStream.close();
return bytes;
}
public static void main(String[] args)
throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException,
InvocationTargetException {
//父加載器設置爲應用程序類加載器
MyClassLoader myClassLoader = new MyClassLoader("/Users/shanyangyang/BaiduNetdiskDownload/java");
Class<?> aClass = myClassLoader.loadClass("com.ysy.");
Object object = aClass.newInstance();
Method method = aClass.getDeclaredMethod("sout", null);
method.invoke(object, null);
}
}
}
打破雙親委派機制
繼承ClassLoader類,重寫laodClass方法
package com.ysy.JVM類加載;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author shanyangyang
* @date 2020/7/2
*/
public class MyClassLoaderTest {
static class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
@Override 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();
//註釋掉委託父類加載器的邏輯,但是需要加個判斷,只有自己目錄下的文件不委託;否則會導致jar包中類無法加載報錯
// 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;
}
}
@Override protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
//該方法將一個字節數組轉換爲Class對象,這個字節數組就是最終讀取的字節數組
return defineClass(name, data, 0, data.length);
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
private byte[] loadByte(String name) throws IOException {
name.replace("\\.", "/");
FileInputStream fileInputStream = new FileInputStream(classPath + "/" + name + ".class");
int len = fileInputStream.available();
byte[] bytes = new byte[len];
fileInputStream.read(bytes);
fileInputStream.close();
return bytes;
}
public static void main(String[] args)
throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException,
InvocationTargetException {
//父加載器設置爲應用程序類加載器
MyClassLoader myClassLoader = new MyClassLoader("/Users/shanyangyang/BaiduNetdiskDownload/java");
Class<?> aClass = myClassLoader.loadClass("com.ysy.");
Object object = aClass.newInstance();
Method method = aClass.getDeclaredMethod("sout", null);
method.invoke(object, null);
}
}
}
模擬tomcat,打破雙親委派機制,實現隔離
通過查看tomcat類加載器模型我們可以得出如下結論:
1、不同的webapps類加載器之間相互隔離,實現了web應用的安全隔離;
2、同時可以通過SharedclassLoader實現類在不同webapp之間的共享;
3、catlinaclassloader下的類又與應用之間的類之間隔離;
4、每個JSP類加載器只加載對應的一個JSP文件;jsp文件熱加載的原理,就是tomcat檢測到jsp文件發生了變化,就刪除舊的jsp類加載器,重新實例化一個JSP類加載器來加載文件。
package com.company.com.threadlocal;
import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author
* @date 2020/7/3
*/
public class MyClassLoaderTest {
static class MyClassLoader extends ClassLoader{
String path;
public MyClassLoader(String path) {
this.path = path;
}
@Override
public 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();
if (!name.startsWith("com.company")){
c = this.getParent().loadClass(name);
} else {
c = findClass(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;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = new byte[0];
try {
data = loadByte(name);
} catch (Exception e) {
e.printStackTrace();
}
return defineClass(name,data,0,data.length);
}
private byte[] loadByte(String name) throws Exception{
name = name.replaceAll("\\.","/");
FileInputStream fis = new FileInputStream(path+"/"+name+".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
MyClassLoader classLoader1 = new MyClassLoader("D:/test");
Class<?> class1 = classLoader1.loadClass("com.company.Test");
Object obj = class1.newInstance();
Method method1 = class1.getDeclaredMethod("sout",null);
method1.invoke(obj,null);
System.out.println(class1.getClassLoader().getClass().getName());
System.out.println("=========================================");
MyClassLoader classLoader2 = new MyClassLoader("D:/test1");
Class<?> class2 = classLoader2.loadClass("com.company.Test");
Object obj1 = class2.newInstance();
Method method2 = class2.getDeclaredMethod("sout",null);
method1.invoke(obj,null);
System.out.println(class2.getClassLoader());
}
}
}