java 虚拟机 初始化_【深入理解Java虚拟机】类的初始化过程
类的初始化过程
类的加载过程.png
加载
将 Class 文件以二进制的形式加载到内存中
验证
校验 Class 文件是否安全,是否被正确的修改等
准备
为类变量申请内存,设置默认值,(初始化变量的默认值,比如int初始化为0,reference初始化为null) 但是达到类的初始化之前都没有初始化为真正的值。
零值.png
解析
将符号引用转换为直接引用
初始化
搜集并执行static代码块,以及 方法的执行,是静态变量以及static 代码块组成
使用
为新对象申请内存, 为示例变量初始化默认值,为实例对象正确的设置初值;生成方法
卸载
在运行的时候加上虚拟机参数 +XX:+TraceClassLoading 可以详细的看到类加载的信息,同样的要看类卸载的信息,可以使用 -XX:TraceClassUnloading
主动引用与被动引用
主动引用
遇到new , getstatic , putstatic,invokestatic 字节码指令的时候,如果没有初始化,进行性初始化
反射的时候,比如: System.load("xxxx.xxxx.xx");
初始化一个类,但是这个类的父类没有初始化的时候(一个接口初始化的时候并不要求其父接口全部初始化)
JVM 需要执行的主类
遇到动态语言支持的时候
被动引用
通过子类引用父类的 静态变量 或者 静态方法,并不会初始化父类。 通过子类引用父类的静态属性,表示对父类的主动使用,而非对子类的主动使用
通过构造类型的数组,不会初始化此类
直接引用某个类的常亮的类型的时候,并不会对该对初始化
示例代码
class SuperClass {
public static String msg = "Hello,World";
static {
System.out.println("SuperClass.static initializer");
}
}
class SubClass extends SuperClass {
public static String msg2 = "Hello,World";
static {
System.out.println("SubClass.static initializer");
}
}
验证通过子类引用父类的常量 不会初始化子类
// 验证通过子类引用父类的常量并不会初始化子类
System.out.println(SubClass.msg);
验证初始化子类的同时一定会初始化父类
System.out.println(SubClass.msg2);
验证初始类型数组的时候并不初始化该类型
对于数组类型,其类型是JVM运行期间动态生成的,类型为[Lxxxx.xxxx.xxxx.xxxx.SubClasss; 其父类为Object,同理二维数组类型为[[Lxxx.xxx.xxx.SubClass; 其父类型为Object;
SubClass[] subClasses = new SubClass[1];
常量池的引用
源码
public class ReferenceExample002 {
public static void main(String[] args) {
// 实际运行的是 System.out.println(Hello, World);
// 对于只有运行期才能确定的值,仍然会初始化类
System.out.println(ExampleClass.msg);
}
}
class ExampleClass {
public static final String msg = "Hello, World";
static {
System.out.println("ExampleClass.static initializer");
}
}
代码反编译后的信息中移除了对ExampleClass 的直接引用
public class ReferenceExample002 {
public ReferenceExample002() {
}
public static void main(String[] args) {
System.out.println("Hello, World");
}
}
反编译ReferenceExample002得到的助记符信息如下:
反编译命令如: javap -c xxx.xxx.xxx
public class com.zhoutao.example.ReferenceExample002 {
public com.zhoutao.example.ReferenceExample002();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String Hello, World
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
编译期间不确定的常量值所在的类将被初始化
package com.zhoutao.classload;
import java.util.UUID;
public class ReferenceExample003 {
public static void main(String[] args) {
System.out.println(ExampleClass.uuid);
}
static class ExampleClass {
// 此处引用的并非真正的常量值
public static final String uuid = UUID.randomUUID().toString();
static {
System.out.println("ExampleClass.static initializer");
}
}
}
ExampleClass 的静态代码块将会被执行: uuid 的值在编译器并不能确定,所以仍然会初始化对应的类,注意和编译期间确定的常量值进行区分。
接口中的常量
当一个接口初始化时候,并不要求其父接口都完成初始化,然后类的初始化的时候要求父类完成初始化,而类的初始化的时候要求父类完成初始化
public class ReferenceExample005 {
public static void main(String[] args) {
System.out.println(SubInterface.b);
}
static interface ParentInterface {
public static int a = 1;
}
static interface SubInterface extends ParentInterface {
public static int b = 2;
}
}
JVM 中父接口并不会因为子接口或者其实现类的初始化而初始化,其仅仅在使用该接口的静态变量的时候才会进行初始化
初始化过程的方法
Java 会为每个类生成 方法 ,对于静态变量会生出 方法
public class ReferenceExample006 {
public static void main(String[] args) {
ExampleClass exampleClass = ExampleClass.getInstance();
System.out.println("a = " + ExampleClass.a);
System.out.println("b = " + ExampleClass.b);
}
static class ExampleClass {
public static int a;
public static int b = 0;
private static ExampleClass exampleClass = new ExampleClass();
private ExampleClass() {
a++;
b++;
}
public static ExampleClass getInstance() {
return exampleClass;
}
}
}
对于上面的代码,可以很简单的知道,其输出值为:
a = 1
b = 1
如果将 ExampleClass 中的定义 b 放置于ExampleClass 的私有构造方法之后,那么其输出的值将为:
a = 1
b = 0
这是因为在方法的搜集static定义及以及代码块的时候,是按照顺序执行的,在私有构造方法时候,将会对b 进行赋值为1,然后在下一步 public static int b = 0; 又重新的将 b 定义为1
本文由博客群发一文多发等运营工具平台 OpenWrite 发布
java 虚拟机 初始化_【深入理解Java虚拟机】类的初始化过程相关推荐
- java 常量折叠_深入理解Java虚拟机之早期编译器优化
Javac编译器 Javac编译器是一个由Java语言编写的程序 Javac的源码与调试 从Sun Javac的代码来看,编译器大致分为3个过程: 解析与填充符号表的过程 插入式注解处理器的注解处理过 ...
- java 准备 解析_深入理解JAVA虚拟机学习笔记24——类加载的准备和解析
每天进步一点点! 今天我们一起看一下类加载的准备阶段和解析阶段. 先看一下准备阶段:主要任务是在方法区中为类变量(仅static修饰变量,不包含实例变量)分配内存并设置类变量初始化的阶段. 这里面的区 ...
- java虚拟机编程_深入理解Java虚拟机(一)
一.运行时数据区域 1.程序计数器: 当前线程执行字节码的行号指示器(通过改变计数器的值来选择下条需要执行的字节码指令) 每个线程有独立的程序计数器(线程私有,为了切换线程时能恢复到挣钱的执行位置) ...
- sas java 虚拟机异常_深入理解JAVA虚拟机之异常诊断
常见的JAVA虚拟机HotSpot虚拟机运行时数据库由5部分构成:方法区,堆,虚拟机栈,本地方法栈,程序计数器.下面列举各个部分可能出现的异常及其出现原因. 1.方法区存放的已被虚拟机加载的类型信息, ...
- java读书心得_深入理解Java虚拟机阅读心得(三)
Java中提倡的自动内存管理最终可以归结为自动化的解决两个问题: 给对象分配内存 回收分配给对象的内存 先说说回收这一方面的两个主要知识点 一.垃圾收集算法 1.标记-清理算法 首先标记出所有需要回收 ...
- 深入理解java虚拟机 新生代_深入理解java虚拟机:笔记
1.运行时数据区域 1.程序计数器 当前线程执行字节码的行号指示器,字节码解释器工作通过改变这个计数器的值来选取下一条需要执行的字节码指令,每一个线程拥有独立的程序计数器,线程私有的内存 2.虚拟机栈 ...
- java虚拟机现状_深入理解java虚拟机的故障处理工具
前言 本文主要给大家介绍的是java虚拟机的故障处理工具,文中提到这些工具包括: 名称 主要作用 jps JVM process Status Tool, 显示指定系统内所有的HotSpot虚拟机进程 ...
- java 异常机制_深入理解Java异常处理机制
一.引子 try-catch-finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过,我亲自体验的"教训"告诉我,这个东西可不是想象中 ...
- java class教程_深入理解Java Class文件格式(七)
本专栏列前面的一系列博客, 对Class文件中的一部分数据项进行了介绍. 本文将会继续介绍class文件中未讲解的信息. 先回顾一下上面一篇文章. 在上一篇博客中, 我们介绍了: this_class ...
- java事件处理模型_从零开始理解JAVA事件处理机制(3)
我们连续写了两小节的教师-学生的例子,必然觉得无聊死了,这样的例子我们就是玩上100遍,还是不知道该怎么写真实的代码.那从本节开始,我们开始往真实代码上面去靠拢. 事件最容易理解的例子是鼠标事件:我们 ...
最新文章
- KVM中virtio之vring(八)
- mpandroidchart y轴从0开始_从零开始学Pytorch(十七)之目标检测基础
- 在 CAP 中使用 AOP ( Castle.DynamicProxy )
- php将文件夹压缩成zip文件,将文件夹压缩成zip文件的php代码_php实例
- eclipse要修改的配置
- java导出csv文件_java导出生成csv文件的方法
- 台式电脑连接蓝牙耳机_怎样知道电脑(台式机)有没有蓝牙?
- createsolidcaret 后 很快就不闪烁了_【文献推送】Adv. Mater. | 单分散硅基闪烁体实现X射线介导的深层肿瘤光动力治疗...
- android studio 2.2.3 ndk 添加 C 和 C++ 代码
- Android Studio - xml布局文件不显示代码怎么办?
- Android-创建简单登陆界面
- Linux之shell命令
- Mybatis源码分析(一) JDBC Mybatis 简介
- 接口性能测试方案分析
- VM VirtualBox Centos6.5安装Oracle 11g r2 RAC
- UITableView 部分方法详解
- ConnectBot的使用
- Processing 模拟池塘生态系统
- 做一个优秀的时间管理者
- 有时感觉自己很是虚伪