JVM学习笔记2

bilibili-深入理解JVM 虚拟机 学习笔记

JVM学习笔记 1

P6 _常量的本质含义与反编译及助记符详解(6)

虚拟机参数小笔记:

-XX:+ 表示开启 option 选项
-XX:- 表示关闭 option 选项
-XX:= 表示 option 选项的值设置为 value

例如:

-XX:+TraceClassLoading 开启打印加载的类列表,用于追踪类的加载信息,默认是不开启的;


package new_package.jvm.p6;

public class MyTest2 {
    public static void main(String[] args) {
        System.out.println(MyParent2.TEXT);
    }
}

class MyParent2 {
    public static final String TEXT = "hello world";

    static {
        System.out.println("MyParent2 static block");
    }
}

// 执行结果
// hello world

思考:为何没有打印 MyParent2 static block ?

常量(public static final xxx xxx = xxx)在编译阶段会存到调用这个常量的方法所在的类的常量池中;
本质上, MyTest2 类并没有直接引用定义常量的类(MyParent2),因此不会触发定义常量的类的初始化;
这里指的是将常量放到 MyTest2 的常量池中,之后 MyTest2 与 MyParent2 不会有引用关系了,MyParent2.class 的存在与否也不会影响 MyTest2.class 的执行。

通过反编译可以看出些更多的信息:

javap -c target.classes.new_package.jvm.p6.MyTest2

public class new_package.jvm.p6.MyTest2 {
  public new_package.jvm.p6.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 hello world
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

通过 3: ldc #4 这一行可以看到, MyTest2.class 并没有引用其他类,而是直接将String hello world推送至栈顶;

ldc 为 jvm 助记符,详情请查看 java虚拟机规范中关于 ldc 的表述


package new_package.jvm.p6;

public class MyTest3 {
    public static void main(String[] args) {
        System.out.println(MyParent3.CONTEXT);
    }
}

class MyParent3 {

    public static String CONTEXT = "MyParent2 context";
    
    static {
        System.out.println("MyParent2 static block");
    }
}

// MyParent2 static block
// MyParent2 context

javap -c target.classes.new_package.jvm.p6.MyTest3

public class new_package.jvm.p6.MyTest3 {
  public new_package.jvm.p6.MyTest3();
    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: getstatic     #3                  // Field new_package/jvm/p6/MyParent3.CONTEXT:Ljava/lang/String;
       6: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       9: return
}

用 MyTest3 和 MyTest2 进行对比可以发现,MyTest3 中 3: getstatic #3并没有直接使用本地字符串缓存,而是引用指向了 MyParent3.CONTEXT;


了解一些其他的助记符:

package new_package.jvm.p6;

public class MyTest4 {
    public static void main(String[] args) {
        int a = MyParent4.a;
        int b = MyParent4.b;
        int c = MyParent4.c;
        int d = MyParent4.d;
        int e = MyParent4.e;
        short s = MyParent4.s;
        float f = MyParent4.f;
        //System.out.println(String.format("%d %d %d %d %s %f", a, b, c, d, s, f));
    }
}

class MyParent4 {
    public static final String TEXT = "hello world";

    public static final int a = 1;
    public static final int b = 5;
    public static final int c = 127;
    public static final int d = 128;
    public static final int e = 168434;

    public static final short s = 200;
    public static final float f = 200F;
}

javap -c target.classes.new_package.jvm.p6.MyTest4

public class new_package.jvm.p6.MyTest4 {
  public new_package.jvm.p6.MyTest4();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_5
       3: istore_2
       4: bipush        127
       6: istore_3
       7: sipush        128
      10: istore        4
      12: ldc           #3                  // int 168434
      14: istore        5
      16: sipush        200
      19: istore        6
      21: ldc           #4                  // float 200.0f
      23: fstore        7
      25: return
}

助记符:

ldc 表示将 int、float、string 类型的常量值从常量池推送到栈顶
iconst_x (x 表示 1~5) 将常量值 x 推送至栈顶
bipush 将单字节(-128~127)常量值推送到栈顶
sipush 将一个短整型(-32768~32767)常量值推送到栈顶

更多信息与助记符参考虚拟机规范中的第六章 Java虚拟机指令集

P7 _编译期常量与运行期常量的区别及数组创建本质分析(7)

package new_package.jvm.p7;

import java.util.UUID;

public class Test3 {

    public static void main(String[] args) {
        System.out.println(Parent3.UU);
    }
}

class Parent3 {
    public static String UU = UUID.randomUUID().toString();

    static {
        System.out.println("Parent3 static block");
    }
}

// Parent3 static block
// 9438293f-3b78-499f-a585-2aa512a97137

javap -c target.classes.new_package.jvm.p7.Test3

public class new_package.jvm.p7.Test3 {
  public new_package.jvm.p7.Test3();
    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: getstatic     #3                  // Field new_package/jvm/p7/Parent3.UU:Ljava/lang/String;
       6: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       9: return
}

当一个常量的值在编译期间无法确定,那么常量值就不会放到调用类的常量池中;
程序运行时 Test4 会主动使用常量所在类 Parent4 ,所以 Parent4 会被初始化;


package new_package.jvm.p7;

public class Test4 {
    public static void main(String[] args) {
//        Parent4 a1 = new Parent4();
//        Parent4 a2 = new Parent4();
        Parent4[] arr = new Parent4[2];
    }
}

class Parent4 {
    static {
        System.out.println("Parent4 static block");
    }
}

// 无输出
public class new_package.jvm.p7.Test4 {
  public new_package.jvm.p7.Test4();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_2
       1: anewarray     #2                  // class new_package/jvm/p7/Parent4
       4: astore_1
       5: return
}


package new_package.jvm.p7;

public class Test4 {
    public static void main(String[] args) {
        Parent4 a1 = new Parent4();
        System.out.println(a1.getClass());

        Parent4[] arr = new Parent4[2];
        System.out.println(arr.getClass());

        Parent4[][] arr2 = new Parent4[2][1];
        System.out.println(arr2.getClass());

        System.out.println(arr.getClass().getSuperclass());
        System.out.println(arr2.getClass().getSuperclass());

        int[] ints = new int[1];
        System.out.println(ints.getClass());
    }
}

class Parent4 {
}

// class new_package.jvm.p7.Parent4
// class [Lnew_package.jvm.p7.Parent4;
// class [[Lnew_package.jvm.p7.Parent4;
// class java.lang.Object
// class java.lang.Object
// class [I
public class new_package.jvm.p7.Test4 {
  public new_package.jvm.p7.Test4();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class new_package/jvm/p7/Parent4
       3: dup
       4: invokespecial #3                  // Method new_package/jvm/p7/Parent4."<init>":()V
       7: astore_1
       8: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      11: aload_1
      12: invokevirtual #5                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      15: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      18: iconst_2
      19: anewarray     #2                  // class new_package/jvm/p7/Parent4
      22: astore_2
      23: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      26: aload_2
      27: invokevirtual #5                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      30: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      33: iconst_2
      34: iconst_1
      35: multianewarray #7,  2             // class "[[Lnew_package/jvm/p7/Parent4;"
      39: astore_3
      40: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      43: aload_3
      44: invokevirtual #5                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      47: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      50: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      53: aload_2
      54: invokevirtual #5                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      57: invokevirtual #8                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
      60: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      63: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      66: aload_3
      67: invokevirtual #5                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      70: invokevirtual #8                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
      73: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      76: iconst_1
      77: newarray       int
      79: astore        4
      81: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      84: aload         4
      86: invokevirtual #5                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      89: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      92: return
}

对于数组实例来说,其类型是由 jvm 在运行期动态生成的,表示为[Lnew_package.jvm.p7.Parent4这种形式;

助记符:
anewarray 创建一个引用类型的数组,并将其引用值压入栈顶()
newarray 创建一个指定的原生类型的数组,并将其引用值压入栈顶(int,char,float,double,boolean,short,long,byte)

P8 _接口初始化规则与类加载器准备阶段和初始化阶段的重要意义分析(8)

package new_package.jvm.p8;

import java.util.Random;

public class MyTest5 {

    public static void main(String[] args) {
        System.out.println(MyParent5.b);
    }
}

interface MyParent5 {
    // 如果是一个确定值,则会直接放到调用类的常量池中,就不会再初始化该父接口了
    int a = new Random().nextInt(10);
}

interface MyChild5 extends MyParent5 {
    // 注意:接口中的参数,默认为 public static final
    int b = 12;
}

删除 MyParent5.class 依然能执行不报错;

当一个接口在初始化时,并不要求其父接口也初始化;
只有在真正使用父接口的时候,才会初始化(如引用父接口中的常量);


package new_package.jvm.p8;

public class MyTest6 {

    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        System.out.println(Singleton.c1);
        System.out.println(Singleton.c2);
    }
}

class Singleton {

    public static int c1;
    //public static int c2 = 0;

    private static Singleton singleton = new Singleton();

    private Singleton() {
        c1++;
        c2++; // 体现了准备阶段的意义,有一个初始值就可以使用了,否则会有异常
        // System.out.println(c2);
    }

    public static int c2 = 0;

    public static Singleton getInstance() {
        return singleton;
    }

}

// 1
// 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值