Java 类的加载过程
1.Loading 使用双亲委派机制将 .class加载到内存中
为什么要使用双亲委派
防止加载同一个.class。通过委托去询问上级是否已经加载过该.class,如果加载过了,则不需要重新加载。保证了数据安全。
保证核心.class不被篡改。通过委托的方式,保证核心.class不被篡改,即使被篡改也不会被加载,即使被加载也不会是同一个class对象,因为不同的加载器加载同一个.class也不是同一个Class对象。这样则保证了Class的执行安全。
自定义类加载器 加密代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56public class ClassLoaderWithEncription extends ClassLoader {
public static int seed = 0B10110110;
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("com.yzw.jvm.hello");
ClassLoader l = new ClassLoaderWithEncription();
Class clazz = l.loadClass("com.yzw.jvm.Hello");
Hello h = (Hello)clazz.newInstance();
h.m();
System.out.println(l.getClass().getClassLoader());
System.out.println(l.getParent());
}
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.replaceAll(".", "/").concat(".msbclass")));
int b = 0;
while((b = fis.read()) != -1) {
fos.write(b ^ seed);
}
fis.close();
fos.close();
}
}如何打破:重写loadClass()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34public class T012_ClassReloading2 {
private static class MyLoader extends ClassLoader {
public Class<?> loadClass(String name) throws ClassNotFoundException {
File f = new File("C:/work/ijprojects/JVM/out/production/JVM/" + name.replace(".", "/").concat(".class"));
if(!f.exists()) return super.loadClass(name);
try {
InputStream is = new FileInputStream(f);
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
e.printStackTrace();
}
return super.loadClass(name);
}
}
public static void main(String[] args) throws Exception {
MyLoader m = new MyLoader();
Class clazz = m.loadClass("com.yzw.jvm.Hello");
m = new MyLoader();
Class clazzNew = m.loadClass("com.yzw.jvm.Hello");
System.out.println(clazz == clazzNew);
}
}打破双亲委派的用途
- ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
- 热启动,热部署
- osgi tomcat 都有自己的模块指定classloader(可以加载同一类库的不同版本)
2. Linking
- Verification
- 验证文件是否符合JVM规定
- Preparation
- 静态成员变量赋默认值
- Resolution
- 将类、方法、属性等符号引用解析为直接引用
常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
- 将类、方法、属性等符号引用解析为直接引用
3.Initializing
- 调用类初始化代码
,给静态成员变量赋初始值
4. 小总结:
load - 默认值 - 初始值
new - 申请内存 - 默认值 - 初始值
java 执行的方式是 混合执行 即编译执行+解释执行 jvm会对热点代码进行编译为本地的可执行文件
编译执行: 代码运行速度快,但第一次运行时速度极慢
解释执行: 代码以运行速度稍慢
-XX:CICompilerCount=n 指定JIT编译器用来编译方法的线程数量
-XX:CompileThreshold = 10000: 一个方法调用多少次会被 HotSpot和JIT 编译器能编译它
-Xcomp: 指定JVM在第一次使用时把所有的字节码编译成本地代码. (即CompileThreshold=1)
-Xint 仅仅使用解释模式,不激活JIT编译器 (即CompileThreshold=0)