文章部分图形来源于尚硅谷,内容仅为个人对JVM的理解,如有错误请批评指正
1、第一层:Class文件由类装载子系统(类加载器Class Loader)加载进内存,有三个过程包括加载、链接、初始化。其中链接过程有验证、准备、解析子过程。
2、第二层:运行时数据区包括:
线程共享区域:方法区(落地实现jdk7永久代,jdk8元空间)、堆
线程私有区域:虚拟机栈、本地方法栈、程序计数器
3、第三层:执行引擎、本地方法接口(本地方法库)
三层划分总体结构:
三层划分结构细节:
文章目录
第一层
一、类加载过程
1、加载:通过类的全限定名来获取定义此类的二进制流;在内存中生成一个Class对象,作为方法区该类各种数据的访问入口。
获取方式:
①从jar、war包中获取;
②从网络中获取;
③运行时计算机生成:Applet。
2、链接:
①验证
:目的是确保Class文件包含的信息符合虚拟机要求
②准备
:正式为类变量分配内存并设置类变量赋零值的阶段( final
修饰的变量在编译阶段就已经是被赋默认值了,在此阶段就直接显示赋值)。
③解析
:
3、初始化:
其中 clinit 方法是Java编译器自动生成并由JVM调用执行,用于对类中静态变量显示赋值
并执行静态代码块中的代码
。
-
赋默认值:是对静态变量赋默认值,如byte、short、int ->0;boolean-> flase(
布尔类型底层是int,1表示true
) -
显示赋值:赋程序员指定的值,如:static int x = 1;此时显示赋值就是给x变量赋值为1,而不是0.
-
Java编译器生成 clinit 方法的条件:
(1)类中有静态变量,并且要求其中一个静态变量是显示赋值的(如:static int x = 1;)
(2)类中的静态变量,要有一个没有用 final 修饰过。用final 修饰的变量会在链接阶段的准备阶段就会显示赋值
,故不会生成 clinit 再去显示赋值; -
用static final 修改的变量,在链接阶段的准备阶段会直接显示赋值的两种情况::
(1)用static final 修改的基本数据类型
,是在链接阶段的准备阶段就会显示赋值;
(2)用static final 修饰的 String 类型,分为两种:
①用字面量的形式(用双引号)赋值的,是在链接阶段的准备阶段就会显示赋值;
如:static final String str = “HelloWord”;
②用 new String的方式,是在初始化阶段才会显示赋值。
static final String str = new String(“HelloWord”);
除了以上两种情况外基本都是在初始化阶段才会显式赋值(指的是用static final 修改的),可以这么理解,对于用static final 修饰的变量在创建时要使用数组和new关键字的,都是在初始化阶段显式赋值,简单理解就是能在编译器就确定的变量的值就是在链接阶段的准备阶段赋值了。
static + final 修饰的变量在何处进行显示赋值的最终结论:
使用 static + final 修饰,且显示赋值中不涉及到方法或构造器调用的基本数据类型或String类型的显示赋值,是在链接