jvm hostSpot内置多个即时编译器:C1,C2,Graal(java10)
分层编译:
java虚拟机的boolean类型和java语言boolean类型区别
$ echo '
public class Foo {
public static void main(String[] args) {
boolean flag = true;
if (flag) System.out.println("Hello, Java!");
if (flag == true) System.out.println("Hello, JVM!");
}
}' > Foo.java
$ javac Foo.java
$ java Foo
$ java -cp /path/to/asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.1
$ awk 'NR==1,/iconst_1/{sub(/iconst_1/, "iconst_2")} 1' Foo.jasm.1 > Foo.jasm
$ java -cp /path/to/asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm
$ java Foo
java基本类型大小
java类加载流程
加载:是指查找字节流,并且据此创建类的过程,但是对于数组类,没有对应的字节流,是由java虚拟机直接生成的。查找字节流,需要借助类加载器来完成
类加载器:
类的唯一性:类的唯一性是由类加载器实例以及类的全名一同确定的,即便是同一串字节流,经过不同的类加载器加载,会得到两个不同的类
双亲委派模型:每当一个类加载器接收到加载请求时,它先将请求转发给父类加载器,该类加载器会尝试去加载
链接:连接是将创建程的类,合并到java虚拟机中,使之能够执行的过程,分为三个阶段:验证,准备和解析
初始化:在java中,如果直接赋值的静态字段被final修饰,并且它的类型是基本类型或字符串,那么改字段便会被java编译器标记成常量值,其初始化直接由java虚拟机完成,除此外,java编译器则会将所有的静态代码块或赋值操作放到clinit方法中,初始化时为常量值赋值,和执行clinit方法的过程
public class Singleton {
private Singleton() {}
private static class LazyHolder {
static final Singleton INSTANCE = new Singleton();
static {
System.out.println("LazyHolder.");
}
}
public static Object getInstance(boolean flag) {
if (flag) return new LazyHolder[2];
return LazyHolder.INSTANCE;
}
public static void main(String[] args) {
getInstance(true);
System.out.println("----");
getInstance(false);
}
}
使用java -version:class Singleton 查看是类加载顺序,代码中新建数组会导致LazyHolder加载,但是不会去初始化
jvm 方法调用
public class TestMethod {
public void invoke(Object object,Object... args){
System.out.printf("一个object和一个可变参数\n");
}
public void invoke(String object,Object pbj,Object... args){
System.out.printf("一个String和一个object和一个可变参数\n");
}
public static void main(String[] args){
TestMethod testMethod=new TestMethod();
testMethod.invoke(null,1);
testMethod.invoke(null,1,2);
testMethod.invoke(null,new Object[]{1});
}
}
运行结果为:
一个String和一个object和一个可变参数,
一个String和一个object和一个可变参数,
一个object和一个可变参数
java编译器方法选择:针对以上情况,如果java编译器在同一个阶段中找到多个适配方法,那么会选择一个最为贴切的,决定贴切程度是形参类型的继承关系。例如,第一个调用testMethod.invoke(null,1);传入null,由于String是Object的子类,因此java编译器会认为第二个方法比较贴切。
jvm静态绑定和动态绑定:
java字节码指令
调用指令的符号引用:编译过程,java编译器会暂时用符号引用来表示目标方法,符号引用存储在class文件的常量池中,如果目标方法是接口方法,那么改引用是接口符号引用,若不是接口引用,则为非接口符号引用。可以通过“javap -v xx.class” 查看类的常量池
java重新与jvm重写:java 重新与jvm重写并不一致,java编译器会生成桥接方法来弥补这种
public class Merchant {
public Number actionPrice(double price){
return 0;
}
}
public class NativeMerchant extends Merchant {
@Override
public Double actionPrice(double price) {
return 0.0;
}
public static void main(String[] args){
Merchant merchant=new NativeMerchant();
merchant.actionPrice(0.0);
}
}
两个类,在java中判断重新是根据 方法名,和形参是否一致来判断是否重写,而在jvm中则加返回参数是否一致才会判断是否重新,NativeMerchant类继承了Merchant类,重新了actionPrice方法,但是返回参数不一样,在java中是重写,但是在jvm中就不是重新,java编译器会为这个方法做桥接来弥补
通过javap -v 查看字节码:
Last modified Apr 28, 2020; size 503 bytes
MD5 checksum 7a2445c8c2e7af4a3cb04167ec453d20
Compiled from "NativeMerchant.java"
public class NativeMerchant extends Merchant
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #7.#19 // Merchant."":()V
#2 = Methodref #20.#21 // java/lang/Double.valueOf:(D)Ljava/lang/Double;
#3 = Class #22 // NativeMerchant
#4 = Methodref #3.#19 // NativeMerchant."":()V
#5 = Methodref #7.#23 // Merchant.actionPrice:(D)Ljava/lang/Number;
#6 = Methodref #3.#24 // NativeMerchant.actionPrice:(D)Ljava/lang/Double;
#7 = Class #25 // Merchant
#8 = Utf8
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 actionPrice
#13 = Utf8 (D)Ljava/lang/Double;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 (D)Ljava/lang/Number;
#17 = Utf8 SourceFile
#18 = Utf8 NativeMerchant.java
#19 = NameAndType #8:#9 // "":()V
#20 = Class #26 // java/lang/Double
#21 = NameAndType #27:#13 // valueOf:(D)Ljava/lang/Double;
#22 = Utf8 NativeMerchant
#23 = NameAndType #12:#16 // actionPrice:(D)Ljava/lang/Number;
#24 = NameAndType #12:#13 // actionPrice:(D)Ljava/lang/Double;
#25 = Utf8 Merchant
#26 = Utf8 java/lang/Double
#27 = Utf8 valueOf
{
public NativeMerchant();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method Merchant."":()V
4: return
LineNumberTable:
line 7: 0
public java.lang.Double actionPrice(double);
descriptor: (D)Ljava/lang/Double;
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=2
0: dconst_0
1: invokestatic #2 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
4: areturn
LineNumberTable:
line 10: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: new #3 // class NativeMerchant
3: dup
4: invokespecial #4 // Method "":()V
7: astore_1
8: aload_1
9: dconst_0
10: invokevirtual #5 // Method Merchant.actionPrice:(D)Ljava/lang/Number;
13: pop
14: return
LineNumberTable:
line 14: 0
line 15: 8
line 16: 14
public java.lang.Number actionPrice(double);
descriptor: (D)Ljava/lang/Number;
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=3, locals=3, args_size=2
0: aload_0
1: dload_1
2: invokevirtual #6 // Method actionPrice:(D)Ljava/lang/Double;
5: areturn
LineNumberTable:
line 7: 0
}
可以看到字节码最后ACC_BRIDGE 表示这是一个桥接方法 ACC_SYNTHETIC表示这是由java编译器生成的方法,在改方法里面,执行invokevirtual #6 调用类方法NativeMerchant.actionPrice:(D)Ljava/lang/Double;
泛型导致形参类型不一致重新
public interface Customer {
boolean isVIP();
}
public class VipCustomer implements Customer {
public boolean isVIP() {
return false;
}
}
public class Merchant {
public Number actionPrice(double price,T customer){
return 0;
}
}
public class NativeMerchant extends Merchant {
@Override
public Double actionPrice(double price, com.zp.jvm.VipCustomer vipCustomer) {
return 0.0;
}
public static void main(String[] args){
Merchant merchant=new NativeMerchant();
merchant.actionPrice(0.0,new VipCustomer());
}
}
反编译NativeMerchant类文件获取到自己码
Constant pool:
#1 = Methodref #9.#34 // com/zp/jvm/Merchant."":()V
#2 = Methodref #35.#36 // java/lang/Double.valueOf:(D)Ljava/lang/Double;
#3 = Class #37 // com/zp/jvm/NativeMerchant
#4 = Methodref #3.#34 // com/zp/jvm/NativeMerchant."":()V
#5 = Class #38 // com/zp/jvm/VipCustomer
#6 = Methodref #5.#34 // com/zp/jvm/VipCustomer."":()V
#7 = Methodref #9.#39 // com/zp/jvm/Merchant.actionPrice:(DLcom/zp/jvm/Customer;)Ljava/lang/Number;
#8 = Methodref #3.#40 // com/zp/jvm/NativeMerchant.actionPrice:(DLcom/zp/jvm/VipCustomer;)Ljava/lang/Double;
#9 = Class #41 // com/zp/jvm/Merchant
#10 = Utf8
#11 = Utf8 ()V
#12 = Utf8 Code
#13 = Utf8 LineNumberTable
#14 = Utf8 LocalVariableTable
#15 = Utf8 this
#16 = Utf8 Lcom/zp/jvm/NativeMerchant;
#17 = Utf8 actionPrice
#18 = Utf8 (DLcom/zp/jvm/VipCustomer;)Ljava/lang/Double;
#19 = Utf8 price
#20 = Utf8 D
#21 = Utf8 vipCustomer
#22 = Utf8 Lcom/zp/jvm/VipCustomer;
#23 = Utf8 main
#24 = Utf8 ([Ljava/lang/String;)V
#25 = Utf8 args
#26 = Utf8 [Ljava/lang/String;
#27 = Utf8 merchant
#28 = Utf8 Lcom/zp/jvm/Merchant;
#29 = Utf8 (DLcom/zp/jvm/Customer;)Ljava/lang/Number;
#30 = Utf8 Signature
#31 = Utf8 Lcom/zp/jvm/Merchant;
#32 = Utf8 SourceFile
#33 = Utf8 NativeMerchant.java
#34 = NameAndType #10:#11 // "":()V
#35 = Class #42 // java/lang/Double
#36 = NameAndType #43:#44 // valueOf:(D)Ljava/lang/Double;
#37 = Utf8 com/zp/jvm/NativeMerchant
#38 = Utf8 com/zp/jvm/VipCustomer
#39 = NameAndType #17:#29 // actionPrice:(DLcom/zp/jvm/Customer;)Ljava/lang/Number;
#40 = NameAndType #17:#18 // actionPrice:(DLcom/zp/jvm/VipCustomer;)Ljava/lang/Double;
#41 = Utf8 com/zp/jvm/Merchant
#42 = Utf8 java/lang/Double
#43 = Utf8 valueOf
#44 = Utf8 (D)Ljava/lang/Double;
{
public com.zp.jvm.NativeMerchant();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method com/zp/jvm/Merchant."":()V
4: return
LineNumberTable:
line 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/zp/jvm/NativeMerchant;
public java.lang.Double actionPrice(double, com.zp.jvm.VipCustomer);
descriptor: (DLcom/zp/jvm/VipCustomer;)Ljava/lang/Double;
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=4, args_size=3
0: dconst_0
1: invokestatic #2 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
4: areturn
LineNumberTable:
line 12: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/zp/jvm/NativeMerchant;
0 5 1 price D
0 5 3 vipCustomer Lcom/zp/jvm/VipCustomer;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=5, locals=2, args_size=1
0: new #3 // class com/zp/jvm/NativeMerchant
3: dup
4: invokespecial #4 // Method "":()V
7: astore_1
8: aload_1
9: dconst_0
10: new #5 // class com/zp/jvm/VipCustomer
13: dup
14: invokespecial #6 // Method com/zp/jvm/VipCustomer."":()V
17: invokevirtual #7 // Method com/zp/jvm/Merchant.actionPrice:(DLcom/zp/jvm/Customer;)Ljava/lang/Number;
20: pop
21: return
LineNumberTable:
line 15: 0
line 16: 8
line 17: 21
LocalVariableTable:
Start Length Slot Name Signature
0 22 0 args [Ljava/lang/String;
8 14 1 merchant Lcom/zp/jvm/Merchant;
public java.lang.Number actionPrice(double, com.zp.jvm.Customer);
descriptor: (DLcom/zp/jvm/Customer;)Ljava/lang/Number;
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=4, args_size=3
0: aload_0
1: dload_1
2: aload_3
3: checkcast #5 // class com/zp/jvm/VipCustomer
6: invokevirtual #8 // Method actionPrice:(DLcom/zp/jvm/VipCustomer;)Ljava/lang/Double;
9: areturn
LineNumberTable:
line 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/zp/jvm/NativeMerchant;
}
Signature: #31 // Lcom/zp/jvm/Merchant;
SourceFile: "NativeMerchant.java"
public java.lang.Number actionPrice(double, com.zp.jvm.Customer);
descriptor: (DLcom/zp/jvm/Customer;)Ljava/lang/Number;
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=4, args_size=3
0: aload_0
1: dload_1
2: aload_3
3: checkcast #5 // class com/zp/jvm/VipCustomer
6: invokevirtual #8 // Method actionPrice:(DLcom/zp/jvm/VipCustomer;)Ljava/lang/Double;
9: areturn
LineNumberTable:
line 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/zp/jvm/NativeMerchant;
}