类加载时机(主动引用)
- new、getStatic、putStatic,invokeStatic指令时加载。
- 反射调用时,如果类没有进行init,会先init。
- 作为父类加载。
- JVM启动时指定的主类。
java.lang.invoke.MethodHandler
被动引用
- 通过子类引用父类的静态字段,不会加载子类。
- 定义引用类数组,底层是
Object
类型不会加载引用类型。 - 使用类的静态常量池不会触发类加载。
类加载过程
- 加载:
- 通过类的全限定名来获取类的二进制字节流。
- 将字节码静态结构转化为方法区的运行时数据结构。
- 创建一个Class类对象作为数据访问的入口。
- 验证:
- 文件格式校验:魔数、JVM版本信息、常量池中数据类型。
- 元数据验证:语法分析,分析是否有语法错误。
- 字节验证:语义分析,分析数据流和控制流。
- 符号引用验证:验证是否能找到对应的类。
- 准备:为类对象在方法区中分配内存。
- 解析:将符号引用转化为直接引用,类、接口、字段、方法解析。
- 初始化:
- 收集类变量赋值语句、静态代码块,合并成clinit并执行。
- JVM保证clinit方法加锁。
- 静态块中不能访问后面定义的类变量,但是可赋值。
类加载器
- 启动类加载器:
JAVA_HOME\lib
、或Xbootclasspath
参数限定的类。 - 扩展类加载器:加载
\lib\ext
,或者java.ext\dirs
系统变量指定的类。 - 应用程序类加载器:加载ClassPath中类库。
- 自定义类加载器。
反射原理
- 理论基础:class对象包含类的所有信息,可以通过class对象获得构造方法,成员变量、成员方法接口等信息。
- 缓存优化:从Class对象中获取的Method、Field等类的组成元素的时候获取到的实际上是该Class对象内部的一个缓存中存储的Method、Field的拷贝。
- 调用优化:使用代理模式当调用次数超过15次后切换native为JDK方式。native方法启动快执行慢,JDK方法启动慢,执行快。
- 线程安全:缓存通过CAS保证线程安全,调用优化中无状态也是线程安全。
- 内存问题:多线程同时调用同一个Method,可能产生多个MethodAccessor。
- 性能问题:需要校验方法可见性,参数使用Object数组包装,需要进行参数校验。
Java内联函数
- 内联函数可以在函数被调用的地方直接展开,不需要像一般函数调用那样,参数压栈返回时参数出栈以及资源释放等。
- final函数可能会被内联,private方法也可能被内联。
- 大方法展开开销大,所以大方法一般不会被内联。