学习JVM的第一天

1.首先了解JVM(java虚拟机)JREJDK之间的关系

          JDK全称是Java SE Development Kit(Java开发工具),提供了编译和运行Java程序所需的各种资源和工具,包括JRE+java开发工具。

          JRE全称是Java runtime environment(java运行环境),包括虚拟机+java的核心类库。

          JVM是运行java程序的核心虚拟机。

2.  类的加载过程

      在java代码中,类型的加载、连接与初始化过程都在程序运行期间完成的。

       提供了更大的灵活性,增加了更多的可能性。

       类从被加载到虚拟机内存中开始,到卸载出内存结束,它的整个生命周期包括      

              加载:通俗的说,也就是将class文件放到java虚拟机中,让jvm去加载类的二进制数据

              连接:连接可以分为3个步骤

                       验证:验证被加载的类的准确性

                       准备:为类的静态变量分配内存,并将其初始化为默认值

                        解析:把类中的符号引用转换成直接引用。也就是说去解析这个类中所包含的其他类型的引用

             初始化:为类中静态变量赋予正确的初始值

             使用:使用这个类的过程

             卸载:GC的回收

 

3.JVM是什么时候结束生命周期的

      1.执行System.exit()方法

      2.程序的正常执行结束

      3.程序在执行过程中遇到了异常或者错误而被终止

      4.由于操作系统出现错误而导致java虚拟机进程终止

 

4.java程序对于类的使用方式可以分为两种

      主动使用

             1.创建类的实例

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

             3.调用类的静态方法

              4.反射

              5.初始化一个类的字类

              6.java虚拟机启动时被标明为启动类的类

              7.jdk1.7开始提供的动态语言支持:java.lang.invoke.MethodHandle实例的解析结果 

                 REF_getStatic,REF_putStatic,REF_invokeStatic句柄对应的类没有初始化,则初始化。

      被动使用

             除了上面其中情况之外,其他使用java类的方法都被看做类的被动使用,都不会当作类的初始化。

 

   5.类的加载方式

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

               2.通过网络下载class文件

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

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

               5.将java源文件动态编译为class文件

 

 

下面开始对于类的加载过程做个demo,并查看结果

1.

package com.example.demo.test;

public class MyTest1 {

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

}
class MyParent{
    public static String str="hello world";

    static {
        System.out.println("this is MyParent");
    }
}
class MyChild extends MyParent{
    public static String str2="weclome";

    static {
        System.out.println("this is MyChild");
    }
}

执行一下发现结果是

this is MyParent
hello world

也就是说Mychild的静态代码块没有被执行

使用JVM options去查看jvm执行顺序

执行结果如下

可以看出第一个被加载出来的是它的父类parent被加载,又由于它调用的父类的str所以打印了第二行。说明child只是被加载,而没有被初始化。

2.当我去调用str2的时候

package com.example.demo.test;

public class MyTest1 {

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

}
class MyParent{
    public static String str="hello world";

    static {
        System.out.println("this is MyParent");
    }
}
class MyChild extends MyParent{
    public static String str2="weclome";

    static {
        System.out.println("this is MyChild");
    }
}

发现执行结果如下

当它调用MyChild.str2的时候,它首先加载并初始化的是它的父类,所以打印了第一行,然后去加载child,调用str2实现之前实现对child的初始化,也就是打印了第二行,最后再去调用str2。

总结:对于静态字段来说,只有直接定义了该字段的类才会被初始化,当一个类在初始化时,要求其父类全部都初始化完毕。

 

附:-XX:+<option>,表示开启option选项

       -XX:-<option>,表示关闭option选项

       -XX:<option>=<value>,表示将optaion选项的值设置为value

 

下面开始对于类的常量加载过程做个demo,并查看结果

package com.example.demo.test;

public class MyTest1 {

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

}
class MyParent{
    public static String str="hello world";

    static {
        System.out.println("this is MyParent");
    }
}
class MyChild extends MyParent{
    public static final String str2="weclome";

    static {
        System.out.println("this is MyChild");
    }
}

调用str2的常量,执行结果是

也就是说没有进行加载和初始化

直接将child和parent的class文件删除,发现常量在编译阶段会存入到调用这个常量的类的常量池中

 

总结:调用类并没有直接引用到定义常量的类,因此并不会触发,由执行上面代码可以得出结果str2在MyTest1的编译过程中就已经放到MyTest1的类所属的常量池中,之后的调用就和parent和child类无关,甚至可以将parent和child类文件删除。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值