目录
跟踪加载
例1
package com.study.classloader;
public class MyTest1 {
public static void main(String[] args) {
System.out.println(Child1.content1);
}
}
class Parent1 {
public static String content1 = "parent";
static {
System.out.println("parent static block");
}
}
class Child1 extends Parent1{
public static String content2 = "child";
static {
System.out.println("child static block");
}
}
结果:
parent static block
parent
分析:
参见第4篇文章解释
这里Child1没有被初始化,那它有没有被加载呢?
为了研究这个问题,我们添加-XX:+TraceClassLoading参数用于追踪类的加载信息
控制台输出:
[0.003s][warning][arguments] -XX:+TraceClassLoading is deprecated. Will use -Xlog:class+load=info instead.
[0.013s][info ][class,load] opened: /Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/lib/modules
[0.026s][info ][class,load] java.lang.Object source: shared objects file
[0.026s][info ][class,load] java.io.Serializable source: shared objects file
...
[0.153s][info ][class,load] com.study.classloader.MyTest1 source: file:/Users/kevin/Documents/study/github/jvm/target/classes/
...
[0.154s][info ][class,load] java.lang.invoke.VarHandle$AccessType source: shared objects file
[0.154s][info ][class,load] java.lang.invoke.VarHandle$AccessMode source: shared objects file
[0.154s][info ][class,load] com.study.classloader.Parent1 source: file:/Users/kevin/Documents/study/github/jvm/target/classes/
[0.154s][info ][class,load] com.study.classloader.Child1 source: file:/Users/kevin/Documents/study/github/jvm/target/classes/
[0.154s][info ][class,load] java.util.concurrent.atomic.Striped64 source: shared objects file
parent static block[0.154s][info ][class,load] java.util.concurrent.atomic.LongAdder source: shared objects file
...
分析:
第一个被加载的类Object类,因为它是所有类的父类
最先加载了MyTest1类(根据主动使用的第6条,虚拟机启动时被标明为启动类的类,MyTest1会被初始化),然后加载了Parent1类,再加载了Child1类
结论:
Child1类会被加载,但不会被初始化
JVM参数选项
Jvm参数开始固定都会有-XX:
- -XX:+<option>,表示开启默认关闭的选项
- -XX:-<option>,表示关闭默认开启的选项
- -XX:<option>=<value>,表示将option选项的值设置为value
常量的本质含义
例2
package com.study.classloader;
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.content);
}
}
class MyParent2{
public static final String content = "parent";
static {
System.out.println("parent static block");
}
}
结果:
parent
分析:
为什么MyParent2没有初始化,执行static代码块?
属性加上final就是常量,在编译阶段常量就会被存在调用常量的方法所在的类的常量池当中
本质上,调用类MyTest2并没有直接引用到定义常量的类MyParent2,因此并不会触发定义常量的类的初始化
注意:这里指的是将常量content存放到了MyTest2的常量池中,之后MyTest2与MyParent2就没有任何关系了
甚至,我们可以将MyParent2的class文件删除
测试1
将MyTest2.class删除,并执行运行
结果:
错误: 找不到或无法加载主类 com.study.classloader.MyTest2
原因: java.lang.ClassNotFoundException: com.study.classloader.MyTest2
测试2:
重新build项目class文件,然后只删除MyParent2.class,并运行
结果:
parent
分析:
可见编译完成后,MyParent2中的常量就已经被保存在了MyTest2的常量池中了,与MyParent2再无关系
反编译及助记符详解
助记符:
助记符本身由相关的类来操作,如ldc对应com.sun.org.apache.bcel.internal.generic.LDC
- ldc表示将int,float或者是String类型的常量值从常量池中推送至栈顶。
- bipush表示将单字节(-128~127)的常量值推送至栈顶
- sipush表示将一个短整型常量值(-32768~32767)推送至栈顶
- iconst_1表示将int类型1推送至栈顶(只有iconst_m1 和 iconst_0 ~ iconst_5,jvm觉得这几个数字最常用,特殊定义)
ICONST类doc文档介绍
com.sun.org.apache.bcel.internal.generic public class ICONST
extends Instruction
implements ConstantPushInstruction
ICONST - Push value between -1, ..., 5, other values cause an exception
Stack: ... -> ...,
ICONST代码实现
public ICONST(final int i) {
super(com.sun.org.apache.bcel.internal.Const.ICONST_0, (short) 1);
if ((i >= -1) && (i <= 5)) {
super.setOpcode((short) (com.sun.org.apache.bcel.internal.Const.ICONST_0 + i)); // Even works for i == -1
} else {
throw new ClassGenException("ICONST can be used only for value between -1 and 5: " + i);
}
value = i;
}
idc
➜ jvm javap -c target/classes/com/study/classloader/MyTest2.class
Compiled from "MyTest2.java"
public class com.study.classloader.MyTest2 {
public com.study.classloader.MyTest2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()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 parent
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
bipush
package com.study.classloader;
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.s);
}
}
class MyParent2{
public static final String content = "parent";
public static final short s = 127;
static {
System.out.println("parent static block");
}
}
结果:
➜ jvm javap -c target/classes/com/study/classloader/MyTest2.class
Compiled from "MyTest2.java"
public class com.study.classloader.MyTest2 {
public com.study.classloader.MyTest2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: bipush 127
5: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
8: return
}
sipush
package com.study.classloader;
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.i);
}
}
class MyParent2{
public static final String content = "parent";
public static final short s = 127;
public static final int i = 128;
static {
System.out.println("parent static block");
}
}
结果:
➜ jvm javap -c target/classes/com/study/classloader/MyTest2.class
Compiled from "MyTest2.java"
public class com.study.classloader.MyTest2 {
public com.study.classloader.MyTest2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: sipush 128
6: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
9: return
}
iconst_5
package com.study.classloader;
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.m);
}
}
class MyParent2{
public static final String content = "parent";
public static final short s = 127;
public static final int i = 128;
public static final int m = 5;
static {
System.out.println("parent static block");
}
}
结果:
➜ jvm javap -c target/classes/com/study/classloader/MyTest2.class
Compiled from "MyTest2.java"
public class com.study.classloader.MyTest2 {
public com.study.classloader.MyTest2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_5
4: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
7: return
}