1.类编译加载和初始化
1.loading(加载到内存)
2.Linking
1.verification(验证 文件格式 CAFE BABE)
2.preparation(准备静态变量赋默认值,比如int赋0 String赋"")
3.resolution(符号引用转为地址,可以直接访问)
3.initializing(赋我们给的初值,调用静态代码块)
2.怎么知道我的类去哪个类加载器
String.class.getClassLoader();
3.内存发生了什么?
把CAFEBABE...二进制文件 load到内存,和生成的Class对象也进入内存
我们通过Class一个大对象访问我们写的对象(metaspace中)(堆中)(由jvm生成)
反射是通过 Class对象访问二进制 翻译为java代码,所以效率慢
4.类加载器(讲得很清晰)(不是继承关系,只是大小关系)(最好是写各个加载器的包的类,查看他是不是在这个加载器里面)
Bootstrap启动类 加载 lib.rt.jar charset.jar由c++实现 返回为null
Extension拓展类 加载拓展jar包 jre/lib/ext//*.jar 或 -Djava.ext.dirs指定
App 加载classpath指定内容 (自己写的代码)
Custom ClassLoader (自定义的加载器,自己指定加载什么)
xx.class.getClassLoader().getClass().getClassLoader();//加载器的加载器不一定是他的父加载器
ClassLoader classLoader = MyClassLoader.class.getClassLoader();
Class<? extends ClassLoader> aClass = classLoader.getClass();
System.out.println(aClass.getName());//application
ClassLoader classLoader1 = MyClassLoader.class.getClassLoader().getParent();
Class<? extends ClassLoader> aClass1 = classLoader1.getClass();
System.out.println(aClass1.getName());//extension
ClassLoader classLoader2= MyClassLoader.class.getClassLoader().getParent().getParent();
System.out.println(classLoader2);//bootstrapt
5.双亲委派机制(从子到父,从父到子的过程)(主要为了安全性,没有这个东西的话,如果自己写的String,会覆盖原来的代码String,这样写个发邮件把密码发我自己邮箱)
先从custom出发 如果cache有这个内容-没有->App
-没有->Ext--没有->Bootstrap--->返回--在他负责管理的jarclass类中找->Ext
--->App-->custom
图jvm10
6.父加载器(!!!不是类加载器的加载器,也不是类加载器的父类 ,只是成员变量的parent)
xx.class.getClassLoader().getClass().getClassLoader(); //null不一定有
xx.class.getClassLoader().getParent().getParent(); //父加载器 null bootstrap
7.类加载器范围(加载的jar路径) Launcher类源码
图jvm11 验证路径
8.自定义类加载器 ClassLoader的loadClass方法源码(native方法是jvm里面的c++代码)
//加载一个类到内存,然后得到他的名字
//这个是使用App类加载器加载类
Class classLoader = CustomLoader.class.getClassLoader().loadClass("top.jamsee.Cat");
Cat cat = (Cat)classLoader.newInstance();
cat.func1();
//什么时候需要加载一个类 spring动态代理,生成新class到内存中,热部署需要把class到内存中
//一个Loader类先继承ClassLoader重写findClass方法,
//使用,把Hello.class加载进来然后使用他的方法
public class CustomLoader extends ClassLoader {
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
File f = new File("c:/test/", name.replaceAll(".", "/").concat(".class"));
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while ((b = fis.read()) != 0) {
baos.write(b);
}
byte[] bytes = baos.toByteArray();
baos.close();
fis.close();
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
ClassLoader l= new CustomLoader();
Class cat = l.loadClass("top.jamsee.Cat");
Cat cat1= (Cat)cat.newInstance();
}
}
}
9.!!!(拓展)加密class(不想要被别人反编译,定义class的加密,这里使用对一个值异或再异或就会变成原来的一个值)
//下列代码只有加密过程,没有解密过程, ^ 异或2次变为原来的数据
public class T007_MSBClassLoaderWithEncription extends ClassLoader {
public static int seed = 0B10110110;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File f = new File("c:/test/", name.replace('.', '/').concat(".msbclass"));
try {
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while ((b=fis.read()) !=0) {
baos.write(b ^ seed);
}
byte[] bytes = baos.toByteArray();
baos.close();
fis.close();//可以写的更加严谨
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name); //throws ClassNotFoundException
}
public static void main(String[] args) throws Exception {
encFile("top.jamsee.Cat");
ClassLoader l = new T007_MSBClassLoaderWithEncription();
Class clazz = l.loadClass("top.jamsee.Cat");
Cat h = (Cat) clazz.newInstance();
h.func1();
System.out.println(l.getClass().getClassLoader());
System.out.println(l.getParent());
//
// ClassLoader l1=Cat.class.getClassLoader();
// Class aClass = l1.loadClass("top.jamsee.Cat");
// Cat o = (Cat) aClass.newInstance();
// o.func1();
}
//解密
private static void encFile(String name) throws Exception {
File f = new File("c:/test/", name.replace('.', '/').concat(".class"));
FileInputStream fis = new FileInputStream(f);
FileOutputStream fos = new FileOutputStream(new File("c:/test/"+name.replace(".","/").concat(".msbclass")));
int b = 0;
while((b = fis.read()) != -1) {
fos.write(b ^ seed);
}
fis.close();
fos.close();
}
}
10.编译(jvm默认使用混合模式)
1.解释器(启动慢) bytecode intepreter 是变class代码
2.JIP just in time compiler编译器(启动快),有些代码需要编译为本地exe代码
3.混合模式(默认使用,使用jvm参数可以指定 -Xmixed混合 启动快,还能及时编译 -Xint解释模式(变本地代码),-Xcomp纯编译模式):
1. 混合使用解释器和多次使用的代码
2.起始阶段用解释执行
3.热点代码检测 多次调用的方法(方法计数器)和循环(循环计数器) 进行
编译时的代码
例子: 写多个个10万次的循环调用一个方法,run->edit config 指定jvm参数 -Xint纯解释(慢) -Xint纯编译(更快)
11.懒加载(了解) lazyinializing 懒初始化 (按需加载 比如new 出来先加载 final不用加载但是可以取出来)
1.jvm没有规定何时把类加载进来,由你自己指定,load到内存
2.但是严格规定什么时候什么时候必须初始化类
1.new getstatic putstatic invokestatic指令,final变量除外(final不用初始化,直接可以取出)
2.反射
3.初始化子类,父类必须首先初始化
4. 动态语言(这个语言)支持 invoke.MethodHandle的结果为 REF_ getstatic Ref_putstatic REF_invokestatic必须初始化
例子:
class aa{
main(){
P p;//类不会打印静态代码块的内容
P p1=new P(); //会打印,因为new 关键字在懒加载中
sout(p1.i);//可以打印,final关键字,不用加载,但是直接被转化为内存地址,取出值
}
public static class P{
final static int i=8;
static{
sout("aaa");
}
}
}
- 复习
1.ClassLoader源码 findCache-->parent.loadClass-->findClass()
2.自定义类加载器 extends ClassLoader--->overwrite findClass()-->
defineClass(byte[]-->Class clazz)字节数组转化为Class对象
3.加密
4.混合执行
13.JVM检测热点代码参数
-XX:CompileThreshold=10000
14.(了解,面试 直接找下一家)双亲委派机制是可以打破的
1.重写ClassLoader的loadClass()
2.何时打破 jdk1.2 ThreadContextClassLoader
通过thread.setContextClassLoader指定
3.热启动(常用)tomcat里面有自己实现classloader
例如 tomcat 有 多个webapplication, 分别在里面可以放同名的类,(已经打破双亲委派机制)
loadClass两次,第二次不会加载,因为在缓存中
代码图12 121
15.linking
1.verification 验证文件是否符合jvm规范
2.preparation 静态成员赋默认值(0 1 null)
3.resolution是否进行解析 将类 方法属性等符号引用解析为直接引用(常量池的符号引用解析为指针 偏移量内存地址的直接引用)(符号引用--->指针,地址)
16.initializing 调用类初始化代码 给静态成员赋初始值
面试题:(一般不这样写程序,直接static{}赋初值)
class T{
public static int count=2; //(2) count=0
T t=new T(); // (3) count=1
private T(){
count++;
}
}
main(){
sout(T.count);//3 linking( )--->initializing (1)
}
下面是先new对象的情况
class T{
T t=new T(); // (1)preparate count=0 ---> count=1
public static int count=2; // (2) initializing count=2
private T(){
count++;
}
}
main(){
sout(T.count);//2 linking(preparate )--->initializing
}
//load—>默认值—>初始值
//对象 new–申请内存–默认值—初始值