jvm-Java从编码到执行
类加载器
类加载器将Java.class文件加载到内存。
当一个class文件被加载到内存,分为两块,第一块是字节码文件,第二块是class类的对象,指向第一块内存。
字节码文件分配在方法区也叫metaspace,而对象一般分配在堆上。
class文件
class文件是二进制字节流,我们一般以16进制查看class文件,
每4位的二进制可以表示为1位16进制,
也就是两位16进制代表1个字节,
(观察MD5加密结果为16长度字节,转16进制字符串为32位可知)
因为一个字节占8位,这样缩写二进制。
类加载和初始化
loading
类加载器层次
双亲委派机制
原因:(安全)核心的类库只能有顶级加载器去加载、节约资源。
(父加载器:上级加载器,不是加载器的加载器)
linking
verification
验证class文件是否符合jvm规范
preparation
给静态成员变量赋默认值
resolution
将类、变量、方法等符号引用(例如class文件常量池中的指向引用)解析为指向内存的直接引用
initializing
这时会执行静态代码块、给静态成员变量赋初始值
延伸:通过new对象访问普通成员变量时分为两步,
第一步申请分配内存,这一步给成员变量赋默认值,
第二部调用构造方法,这一步给成员变量赋初始值。
lazyloading
jvm虚拟机的实现用的懒加载,什么时候需要用到这个类的时候,
才会去加载。
jvm严格规定了类初始化的时间。
有这样几种情况:
1、遇到new、getstatic、putstatic、invokestatic指令,访问final变量除外;
2、java.lang.reflect对类进行反射调用时;
3、初始化子类时,父类必须首先被初始化;
4、虚拟机启动时,被执行的主类必须被初始化;
测试:
package com.duohoob.jvm.bytecode;
public class ClassLoadingProduce {
public static void main(String[] args) {
System.out.println(T.i); // T没有被加载
System.out.println(T.j); // preparation:j=0,T=null;initializing:j=2,new T()调用构造方法,j++=3。
System.out.println(T.k); // preparation:k=0,T=null;initializing:new T()调用构造方法,k++=1,k=2。
}
}
class T {
public static final int i = 2;
// 从上往下初始化静态变量
public static int j = 2;
public static T t = new T();
public static int k = 2;
public T() {
j++;
k++;
System.out.println("hello");
}
}
输出:
混合模式执行
Java是解释和编译混合执行的,
解释执行和编译执行共存,也可以指定。
编译是指编译为本地代码,例如编译为Linux的可执行程序,或Windows的exe,提高执行效率。
解释执行特点:启动快,
编译执行特点:执行快