JVM理解-day01

-JVM理解

类加载

在Java代码中,类型(class)的加载、连接与初始化过程都是在程序运行期间完成的

在cmd可以输入这两个命令查看jvm的情况

-jconsole

 

 

选择不安全连接

 

-jvisualvm

 

类的加载、连接与初始化

加载

  • 查找并加载类的二进制数据

连接

  • 验证:确保被加载的类的正确性

  • 准备:为类的静态变量分配内存,并将其初始化为默认值(如public static int a = 1),在这一步只是为a分配了内存,且将其初始化为了0(int型默认值为0)

  • 解析:把类中的符号引用转换为直接引用

初始化

  • 为类的静态变量赋予正确的初始值(这时 a=1)

类的使用和卸载

  • 使用

  • 卸载

Java程序对类的使用方式可以分为两种

  • 主动使用

  • 被动使用

所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”的时候才初始化他们

public class MyTest4 {
    public static void main(String[] args) {
        MyParent4 myParent4 = new MyParent4();
        System.out.println(myParent4.getClass());
        System.out.println("=========");
        MyParent4 myParent5 = new MyParent4();
    }
}
​
class MyParent4{
    static{
        System.out.println("MyParent4 static block");
    }
}
MyParent4 static block
class com.qyh.jvm.classloader.MyParent4
=========

可以看到只初始化了一次

主动使用(七种)

  • 创建一个类的实例

  • 访问某个类或接口的静态变量,或者对该静态变量赋值

  • 调用类的静态方法

  • 反射(如Class.forName("com.qyh.Test"))

  • 初始化一个类的子类(初始化子类,就会初始化父类,如果父类还有父类那就一直往上初始化)

  • Java虚拟机启动时被标明为启动类的类(含Main方法)

  • JDK1.7开始提供的动态语言支持

除了以上七种情况,其它使用Java类的方式都被看做是对类的被动使用,不会导致类的初始化

类的加载

类的加载指得是将类的.class文件中的二进制数据读入内存中,将其放入在运行时数据区的方法区(JDK1.8之后移除了方法区,将其移至源空间,位于本地内存中),然后在内存中创建一个java.lang.Class对象(规范并未说明对象位于哪里,Hotspot虚拟机将其放在了方法区中)用来封装在方法区的数据结构

  • 加载.class文件的方式

  1. 从本地系统中直接中加载

  2. 通过网络下载.class 文件

  3. 从zip,jar等归档文件中加载.class文件

  4. 从专有的数据库中提取.class 文件

  5. 将Java源文件动态编译为.class文件(如jsp中的java代码)

package com.qyh.jvm.classloader;
​
public class MyTest1 {
​
    public static void main(String[] args) {
//        System.out.println(MyChild1.str2);
        System.out.println(MyChild1.str);
    }
​
}
​
class MyParent1{
    public static String str="hello world";
    static{
        System.out.println("MyParent1 static block");
    }
}
​
class MyChild1 extends MyParent1{
    public static String str2 = "welcome";
    static{
        System.out.println("MyChild1 static block");
    }
}

运行结果

MyParent1 static block
hello world

思考:虽然MyChild1没有初始化,但是此时MyChild1加载了么?

XX:+TraceClassLoading #用于追踪类加载信息并打印出来

运行,查看控制台打印的信息

 

 

可以看出,MyChild1,尽管没有初始化,但是仍然加载了的!

package com.qyh.jvm.classloader;
​
public class MyTest1 {
​
    public static void main(String[] args) {
        System.out.println(MyChild1.str2);
//        System.out.println(MyChild1.str);
    }
​
}
​
class MyParent1{
    public static String str="hello world";
    static{
        System.out.println("MyParent1 static block");
    }
}
​
class MyChild1 extends MyParent1{
    public static String str2 = "welcome";
    static{
        System.out.println("MyChild1 static block");
    }
}

运行结果

MyParent1 static block
MyChild1 static block
welcome

编译期常量与运行期常量的区别

package com.qyh.jvm.classloader;
​
//常量在编译阶段会被存入到调用这个方法所在的类的常量池中,(str就放在了MyTest2的常量池中)
//本质上,调用类并没有直接引用到定义常量的类,因此并不会触发定义常量的类的初始化
//运行结果仅仅为hello world
//注意:这里只是将常量存放到了MyTest2的常量池中,之后的MyTest2与MyParent2就没有任何关系了
//甚至我们可以吧MyParent2的class文件删除掉,仍然可以运行出结果
​
public class MyTest2 {
​
    public static void main(String[] args) {
        System.out.println(MyParent2.str);
    }
​
}
​
class MyParent2{
    public static final String str="hello world";
​
    static {
        System.out.println("MyParent2 static block");
    }
​
}
​

为了验证删除class文件,我们用记事本编译,运行一次后,删除掉MyParent2的.class文件

 

反编译结果:

D:\Javaweb\jvm_study>cd out
系统找不到指定的路径。
​
D:\Javaweb\jvm_study>cd target
​
D:\Javaweb\jvm_study\target>cd classes
​
D:\Javaweb\jvm_study\target\classes>javap -c com.qyh.jvm.classloader.Mytest2
警告: 二进制文件com.qyh.jvm.classloader.Mytest2包含com.qyh.jvm.classloader.MyTest2
Compiled from "MyTest2.java"
public class com.qyh.jvm.classloader.MyTest2 {
  public com.qyh.jvm.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 hello world
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}
​

关于助记符

package com.qyh.jvm.classloader;
​
//常量在编译阶段会被存入到调用这个方法所在的类的常量池中,(str就放在了MyTest2的常量池中)
//本质上,调用类并没有直接引用到定义常量的类,因此并不会触发定义常量的类的初始化
//运行结果仅仅为hello world
//注意:这里只是将常量存放到了MyTest2的常量池中,之后的MyTest2与MyParent2就没有任何关系了
//甚至我们可以吧MyParent2的class文件删除掉,仍然可以运行出结果
​
//在Terminal反编译后 D:\Javaweb\jvm_study\target\classes>javap -c com.qyh.jvm.classloader.Mytest2
//查看到结果:
/*
    助记符:
     ldc 表示将int,float或是String类型的常量值从常量池推送至栈顶
     bipush 表示将单字节(-128~127)的常量值推送至栈顶
     sipush  表示将一个短整型常量值(-32768~32767)推送至栈顶
     iconst_1   表示将int类型的1推送至栈顶(iconst_0 ~ iconst_5 表示int类型0~5
     iconst_m1  表示将int类型-1推送至栈顶
     超过5或者小于-1之后便会变成bipush以此推递下去
​
 */
​
public class MyTest2 {
​
    public static void main(String[] args) {
        System.out.println(MyParent2.m);
    }
​
}
​
class MyParent2{
​
    public static final String str="hello world";
​
    public static final short s=127;
​
    public static final int i=128;
​
//    public static final int m=1;
//    public static final int m=-2;
//    public static final int m=-1;
    public static final int m=0;
//        public static final int m=5;
//    public static final int m=6;
​
    static {
        System.out.println("MyParent2 static block");
    }
​
}

 

反编译

D:\Javaweb\jvm_study\target\classes>javap -c com.qyh.jvm.classloader.Mytest2
警告: 二进制文件com.qyh.jvm.classloader.Mytest2包含com.qyh.jvm.classloader.MyTest2
Compiled from "MyTest2.java"
public class com.qyh.jvm.classloader.MyTest2 {
  public com.qyh.jvm.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
}
​
D:\Javaweb\jvm_study\target\classes>javap -c com.qyh.jvm.classloader.Mytest2
警告: 二进制文件com.qyh.jvm.classloader.Mytest2包含com.qyh.jvm.classloader.MyTest2
Compiled from "MyTest2.java"
public class com.qyh.jvm.classloader.MyTest2 {
  public com.qyh.jvm.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_1
       4: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       7: return
}
​
D:\Javaweb\jvm_study\target\classes>javap -c com.qyh.jvm.classloader.Mytest2
警告: 二进制文件com.qyh.jvm.classloader.Mytest2包含com.qyh.jvm.classloader.MyTest2
Compiled from "MyTest2.java"
public class com.qyh.jvm.classloader.MyTest2 {
  public com.qyh.jvm.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_2
       4: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       7: return
}
​
D:\Javaweb\jvm_study\target\classes>javap -c com.qyh.jvm.classloader.Mytest2
警告: 二进制文件com.qyh.jvm.classloader.Mytest2包含com.qyh.jvm.classloader.MyTest2
Compiled from "MyTest2.java"
public class com.qyh.jvm.classloader.MyTest2 {
  public com.qyh.jvm.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        6
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       8: return
}
​
D:\Javaweb\jvm_study\target\classes>javap -c com.qyh.jvm.classloader.Mytest2
警告: 二进制文件com.qyh.jvm.classloader.Mytest2包含com.qyh.jvm.classloader.MyTest2
Compiled from "MyTest2.java"
public class com.qyh.jvm.classloader.MyTest2 {
  public com.qyh.jvm.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_m1
       4: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       7: return
}
​
D:\Javaweb\jvm_study\target\classes>javap -c com.qyh.jvm.classloader.Mytest2
警告: 二进制文件com.qyh.jvm.classloader.Mytest2包含com.qyh.jvm.classloader.MyTest2
Compiled from "MyTest2.java"
public class com.qyh.jvm.classloader.MyTest2 {
  public com.qyh.jvm.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        -2
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       8: return
}
​
D:\Javaweb\jvm_study\target\classes>javap -c com.qyh.jvm.classloader.Mytest2
警告: 二进制文件com.qyh.jvm.classloader.Mytest2包含com.qyh.jvm.classloader.MyTest2
Compiled from "MyTest2.java"
public class com.qyh.jvm.classloader.MyTest2 {
  public com.qyh.jvm.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_0
       4: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       7: return
}

注意:

package com.qyh.jvm.classloader;
​
import java.util.UUID;
​
/*
    当一个常量的值并非编译期间可以确定的,那么其值就不会被放到调用类的常量池中,
    这时在程序运行时,会导致主动使用这个常量所在的类,显然会导致这个类初始化
 */
​
public class MyTest3 {
​
    public static void main(String[] args) {
        System.out.println(MyParent3.str);
    }
​
}
​
class MyParent3{
​
    public static final String str = UUID.randomUUID().toString();
​
    static {
        System.out.println("MyParent3 static code");
    }
​
}
​

运行结果:

MyParent3 static code
0a9218e8-c54d-4318-961c-804869f64496
​

数组类型创建的本质分析

package com.qyh.jvm.classloader;
​
/*
    对于数组实例来说,其类型是由JVM在运行期动态生成的,表示为[Lcom.qyh.jvm.classloader.MyParent4
    这种形式。动态生成的类型,其父类型就是Object。
    对于数组来说,JavaDoc经常将构成数组的元素称为Component,实际上就是将数组降低一个维度后的类型。
​
    助记符:
    anewarray 表示创建一个引用类型的(如:类、接口、数组)数组,并将其引用值压入栈顶
    newarray 表示创建一个指定的原始类型(如:int、float、char等)的数组,并将其引用值压入栈顶
 */
​
public class MyTest4 {
    public static void main(String[] args) {
//        MyParent4 myParent4 = new MyParent4();
//        System.out.println(myParent4.getClass());
//        System.out.println("=========");
        MyParent4[] myParent4s = new MyParent4[1];
        System.out.println(myParent4s.getClass());
​
        MyParent4[][] myParent4s1 = new MyParent4[1][1];
        System.out.println(myParent4s1.getClass());
​
        System.out.println(myParent4s.getClass().getSuperclass());
        System.out.println(myParent4s1.getClass().getSuperclass());
​
        System.out.println("===============");
​
        int[] ints = new int[1];
        System.out.println(ints.getClass());
        System.out.println(ints.getClass().getSuperclass());
​
        char[] chars = new char[1];
        System.out.println(chars.getClass());
​
​
        boolean[] b = new boolean[1];
        System.out.println(b.getClass());
​
        short[] s = new short[1];
        System.out.println(s.getClass());
​
        byte[] bytes = new byte[1];
        System.out.println(bytes.getClass());
​
    }
}
​
class MyParent4{
    static{
        System.out.println("MyParent4 static block");
    }
}
​
MyParent4 static block
class com.qyh.jvm.classloader.MyParent4
######################################################
class [Lcom.qyh.jvm.classloader.MyParent4;
class [[Lcom.qyh.jvm.classloader.MyParent4;
class java.lang.Object
class java.lang.Object
===============
class [I
class java.lang.Object
class [C
class [Z
class [S
class [B

反编译:

D:\Javaweb\jvm_study\target\classes>javap -c com.qyh.jvm.classloader.Mytest4
警告: 二进制文件com.qyh.jvm.classloader.Mytest4包含com.qyh.jvm.classloader.MyTest4
Compiled from "MyTest4.java"
public class com.qyh.jvm.classloader.MyTest4 {
  public com.qyh.jvm.classloader.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: anewarray     #2                  // class com/qyh/jvm/classloader/MyParent4
       4: astore_1
       5: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       8: aload_1
       9: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      12: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      15: iconst_1
      16: iconst_1
      17: multianewarray #6,  2             // class "[[Lcom/qyh/jvm/classloader/MyParent4;"
      21: astore_2
      22: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      25: aload_2
      26: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      29: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      32: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      35: aload_1
      36: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      39: invokevirtual #7                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
      42: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      45: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      48: aload_2
      49: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      52: invokevirtual #7                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
      55: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      58: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      61: ldc           #8                  // String ===============
      63: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      66: iconst_1
      67: newarray       int
      69: astore_3
      70: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      73: aload_3
      74: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      77: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      80: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      83: aload_3
      84: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      87: invokevirtual #7                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
      90: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      93: iconst_1
      94: newarray       char
      96: astore        4
      98: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     101: aload         4
     103: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     106: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     109: iconst_1
     110: newarray       boolean
     112: astore        5
     114: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     117: aload         5
     119: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     122: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     125: iconst_1
     126: newarray       short
     128: astore        6
     130: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     133: aload         6
     135: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     138: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     141: iconst_1
     142: newarray       byte
     144: astore        7
     146: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     149: aload         7
     151: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     154: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     157: return
}

结论:

只有主动引用才会初始化类,第一次new MyParent4可以初始化,并且打印对象的类,可以看到来自于com.qyh.jvm.classloader.MyParent4

当我们创建MyParent4的一维和二维数组的时候,并没有主动引用MyParent4这个类,通过getclass我们看到我们创建的实例来自[Lcom.XXX和[[Lcom.XXX这个是虚拟机在编译的时候相当于动态代理给我们实现的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值