简单复习一波JVM底层知识
Java源文件经过javac编译后,形成.class的字节码文件,然后由类加载器ClassLoader加载进JVM虚拟机中
进入运行时数据区后,运行时数据区内部结构如下
- 方法区:又称静态区,被所有线程共享; 存放类文件相关信息,静态变量/方法/代码块相关信息。
- 虚拟机栈:存储的是自定义对象的引用(不是真实对象)和基础数据类型的对象,属于每个线程私有。
- 本地方法栈:与虚拟机栈功能类型,只不过虚拟机栈是为虚拟执行的Java方法(字节码)服务,而本地方法栈是为虚拟机执行的Native方法服务,这边方法偏底层,一般由C/C++编写。
- 堆:存储的是Java的真实对象,不再是引用,当堆中的对象没有被栈中引用,则堆中的对象则会被GC回收,被所有线程所共享。
- 程序计数器:线程私有,随着线程的创建而创建,销毁而销毁。如果一个线程正在执行的是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虚拟就会对其初始化,如下六种情况为主动使用:
- 常见的类对象被实例化(new、反射、克隆、反序列化)
- 调用某个类/接口的静态方法/静态变量时
- 调用Java API中的反射方法,如Class中的方法或者java.lang.reflect方法时
- 初始化某个类的子类
- 当虚拟机启动某个被标明为启动类的类(即包含main方法的那个类)
Java编译器会收集所有的类变量初始化语句和类型的静态初始化器,将这些放到一个特殊的方法中:clinit
具体的内部细节参考
https://blog.csdn.net/qq_39327985/article/details/81839685