Java 类的加载过程

类的加载过程

1.Loading 使用双亲委派机制将 .class加载到内存中

类的加载机制

  1. 为什么要使用双亲委派

     防止加载同一个.class。通过委托去询问上级是否已经加载过该.class,如果加载过了,则不需要重新加载。保证了数据安全。

     保证核心.class不被篡改。通过委托的方式,保证核心.class不被篡改,即使被篡改也不会被加载,即使被加载也不会是同一个class对象,因为不同的加载器加载同一个.class也不是同一个Class对象。这样则保证了Class的执行安全。

  2. 自定义类加载器 加密代码

    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
    56
    public class ClassLoaderWithEncription 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("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();
    }
    }

  3. 如何打破:重写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
    34
    public class T012_ClassReloading2 {
    private static class MyLoader extends ClassLoader {
    @Override
    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);
    }
    }
    1. 打破双亲委派的用途

      1. ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
      2. 热启动,热部署
      3. osgi tomcat 都有自己的模块指定classloader(可以加载同一类库的不同版本)

2. Linking

  1. Verification
    1. 验证文件是否符合JVM规定
  2. Preparation
    1. 静态成员变量赋默认值
  3. Resolution
    1. 将类、方法、属性等符号引用解析为直接引用
      常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用

3.Initializing

  1. 调用类初始化代码 ,给静态成员变量赋初始值

4. 小总结:

  1. load - 默认值 - 初始值

  2. new - 申请内存 - 默认值 - 初始值

  3. java 执行的方式是 混合执行 即编译执行+解释执行 jvm会对热点代码进行编译为本地的可执行文件

​ 编译执行: 代码运行速度快,但第一次运行时速度极慢

​ 解释执行: 代码以运行速度稍慢

​ -XX:CICompilerCount=n 指定JIT编译器用来编译方法的线程数量

​ -XX:CompileThreshold = 10000: 一个方法调用多少次会被 HotSpot和JIT 编译器能编译它

​ -Xcomp: 指定JVM在第一次使用时把所有的字节码编译成本地代码. (即CompileThreshold=1)

​ -Xint 仅仅使用解释模式,不激活JIT编译器 (即CompileThreshold=0)