java 类的加载过程知识点整理

 

一 java编译与反编译生成字节码

1.1 编译CLPreparation.java文件并且反编译生成字节码

CLPreparation的源码:

package com.iqiyi.jvm.serializable;

/**
 * Created by leixingbang on 2019/8/1.
 */
public class CLPreparation {
    public static int a = 111111;//静态变量 
    public static final int INT_CONSTANT = 22222;//原始类型常量 
    public static final Integer INTEGER_CONSTANT = Integer.valueOf(3333333);//引用类型
}

编译并使用javap -v 命令反编译.class文件生成字节码

[root@audit-video-history-1f6396079-1 ~]# javac CLPreparation.java 
[root@audit-video-history-1f6396079-1 ~]# ll
total 24
-rw-r--r-- 1 root root  509 Aug  2 12:12 CLPreparation.class
-rw-r--r-- 1 root root  286 Aug  1 19:37 CLPreparation.java
[root@audit-video-history-1f6396079-1 ~]# javap -v CLPreparation.class 
Classfile /root/CLPreparation.class

抓取字节码的源关键部分源文件,从源文件中可以看出静态变量值的真正初始化(对a进行11111赋值),需要调用putstatic的jvm指令,而使用该指令是在显示初始化时候执行的。加载---验证--准备---解析--初始化--卸载

static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: ldc           #2                  // int 111111
         2: putstatic     #3                  // Field a:I
         5: ldc           #4                  // int 3333333
         7: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        10: putstatic     #6                  // Field INTEGER_CONSTANT:Ljava/lang/Integer;
        13: return
      LineNumberTable:
        line 7: 0
    

1.2双亲委托模型

作用是避免重复加载,造成浪费。

1.3类加载机制的三个特性

  • 双亲委派模型
  • 可见性 子类加载器可以看到父类加载器的类型,反过来不可以。
  • 单一性 父加载器的类型对子类可见,所以父加载器加载过类型子类不会重复加载。

1.4 自定义类的加载过程

  • 步骤(1)通过定义的名称'com.iqiyi.leetcode.CheckBracketsTest'找到对应二进制的实现‘CheckBracketsTest.class’,这里就可以截取,修改字节码
    步骤(2)调用父类的define class 方法,完成类的加载过程。
    

    自定义实现Classloader类:

    /**
     * 文件加载类
     * 可根据MyFileClassLoader 从文件中动态生成类
     *
     * @author chengmingwei
     */
    public class MyFileClassLoader extends ClassLoader {
    
        private String classPath;
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            MyFileClassLoader fileClsLoader = new MyFileClassLoader();
            fileClsLoader.setClassPath("D:\\myPoject\\leetcode\\target\\test-classes\\");
            Class cls = fileClsLoader.loadClass("com.iqiyi.leetcode.CheckBracketsTest");
            Object obj = cls.newInstance();
            Method[] mthds = cls.getMethods();
            for (Method mthd : mthds) {
                String methodName = mthd.getName();
                System.out.println("mthd.name=" + methodName);
            }
            System.out.println("obj.class=" + obj.getClass().getName());
            System.out.println("obj.class=" + cls.getClassLoader().toString());
            System.out.println("obj.class=" + cls.getClassLoader().getParent().toString());
        }
    
        /**
         * 根据类名字符串从指定的目录查找类,并返回类对象
         */
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] classData = null;
            try {
                //步骤(1)通过定义的名称'com.iqiyi.leetcode.CheckBracketsTest'找到对应二进制的实现‘CheckBracketsTest.class’,这里就可以截取,修改字节码
                classData = loadClassData(name);
            } catch (IOException e) {
                e.printStackTrace();
            }
            //步骤(2)调用父类的define class 方法,完成类的加载过程。
            return super.defineClass(name, classData, 0, classData.length);//
    
        }
    
        /**
         * 根据类名字符串加载类 byte 数据流
         *
         * @param name 类名字符串  例如: com.cmw.entity.SysEntity
         * @return 返回类文件 byte 流数据
         * @throws IOException
         */
        private byte[] loadClassData(String name) throws IOException {
            File file = getFile(name);
            FileInputStream fis = new FileInputStream(file);
            byte[] arrData = new byte[(int) file.length()];//一次性读取文件到二进制数组
            fis.read(arrData);
            return arrData;
        }
    
        /**
         * 根据类名字符串返回一个 File 对象
         *
         * @param name 类名字符串
         * @return File 对象
         * @throws FileNotFoundException
         */
        private File getFile(String name) throws FileNotFoundException {//获取文件
            File dir = new File(classPath);
            if (!dir.exists()) throw new FileNotFoundException(classPath + " 目录不存在!");
            String _classPath = classPath.replaceAll("[\\\\]", "/");
            int offset = _classPath.lastIndexOf("/");
            name = name.replaceAll("[.]", "/");
            if (offset != -1 && offset < _classPath.length() - 1) {
                _classPath += "/";
            }
            _classPath += name + ".class";
            dir = new File(_classPath);
            if (!dir.exists()) throw new FileNotFoundException(dir + " 不存在!");
            return dir;
        }
    
        public String getClassPath() {
            return classPath;
        }
    
        public void setClassPath(String classPath) {
            this.classPath = classPath;
        }
    }
    

1.5 从类的加载角度优化java启动(启动为重点)速度。

字节码(.class文件)与平台无关,但是二进制文件加载 需要加载--验证--解析--初始化 这些步骤会导致java的启动速度变慢。

提速的方式:

  • AOT 。编译时直接编译为机器码,降低解析的开销。
  • AppCDS.将通过内存映射直接映射到内存的地址空间,而不是走加载延、验证解析等步骤。
  •  

1.6 java hell 包冲突

同一个类在不同的jar包中有不同的版本。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值