jvm-Java从编码到执行

java每次编译的class完全一样吗 java编译后的class文件是几进制_初始化

类加载器

类加载器将Java.class文件加载到内存。

当一个class文件被加载到内存,分为两块,第一块是字节码文件,第二块是class类的对象,指向第一块内存。
字节码文件分配在方法区也叫metaspace,而对象一般分配在堆上。

class文件

class文件是二进制字节流,我们一般以16进制查看class文件,
每4位的二进制可以表示为1位16进制,
也就是两位16进制代表1个字节,
(观察MD5加密结果为16长度字节,转16进制字符串为32位可知)
因为一个字节占8位,这样缩写二进制。

类加载和初始化

java每次编译的class完全一样吗 java编译后的class文件是几进制_java_02

loading

类加载器层次

java每次编译的class完全一样吗 java编译后的class文件是几进制_jvm_03

双亲委派机制

原因:(安全)核心的类库只能有顶级加载器去加载、节约资源。

java每次编译的class完全一样吗 java编译后的class文件是几进制_加载器_04


(父加载器:上级加载器,不是加载器的加载器)

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每次编译的class完全一样吗 java编译后的class文件是几进制_jvm_05

混合模式执行

Java是解释和编译混合执行的,

解释执行和编译执行共存,也可以指定。
编译是指编译为本地代码,例如编译为Linux的可执行程序,或Windows的exe,提高执行效率。

解释执行特点:启动快,

编译执行特点:执行快

java每次编译的class完全一样吗 java编译后的class文件是几进制_加载器_06


java每次编译的class完全一样吗 java编译后的class文件是几进制_初始化_07