简单复习一波JVM底层知识

Java源文件经过javac编译后,形成.class的字节码文件,然后由类加载器ClassLoader加载进JVM虚拟机中

进入运行时数据区后,运行时数据区内部结构如下

  1. 方法区:又称静态区,被所有线程共享; 存放类文件相关信息,静态变量/方法/代码块相关信息。
  2. 虚拟机栈:存储的是自定义对象的引用(不是真实对象)和基础数据类型的对象,属于每个线程私有。
  3. 本地方法栈:与虚拟机栈功能类型,只不过虚拟机栈是为虚拟执行的Java方法(字节码)服务,而本地方法栈是为虚拟机执行的Native方法服务,这边方法偏底层,一般由C/C++编写。
  4. 堆:存储的是Java的真实对象,不再是引用,当堆中的对象没有被栈中引用,则堆中的对象则会被GC回收,被所有线程所共享。
  5. 程序计数器:线程私有,随着线程的创建而创建,销毁而销毁。如果一个线程正在执行的是Java方法,(如果线程发生上下文切换)则程序计数器记录的就是正在执行的虚拟机字节码指令的地址,如果执行的是native方法,程序计数器值为空。

代码:

public class StaticDemo {
   

    public static  String INIT = "init";  //如果加上final则输出不一样
    static {
   
        System.out.println("------StaticDemo----");
    }
}

class TestStatic {
   

    public static void main(String[] args) {
   
        System.out.println(StaticDemo.INIT);
    }
}

输出:

------StaticDemo----
init

但是如果我在public static String INIT = "init"; //如果加上final则输出不一样

输出:

init

这是因为,在类文件被加载进JVM的时候,静态的方法块被加载进了<mark>方法区里面</mark>,而static final修饰的静态变量被加载进了方法区内部的常量池中,这个常量池中的变量调用都不会触发类的执行,如果没有加final则也是放方法区,常量池外,可以触发类的执行。

总结:static的执行时机是,类文件被加载进了JVM,staic修饰的方法变量被加载进了方法区,都不一定被执行,只有类文件被主动触发执行,static 才可以被执行。

当一个类被主动使用时,Java虚拟就会对其初始化,如下六种情况为主动使用:

  1. 常见的类对象被实例化(new、反射、克隆、反序列化)
  2. 调用某个类/接口的静态方法/静态变量时
  3. 调用Java API中的反射方法,如Class中的方法或者java.lang.reflect方法时
  4. 初始化某个类的子类
  5. 当虚拟机启动某个被标明为启动类的类(即包含main方法的那个类)
    Java编译器会收集所有的类变量初始化语句和类型的静态初始化器,将这些放到一个特殊的方法中:clinit

具体的内部细节参考
https://blog.csdn.net/qq_39327985/article/details/81839685