深入理解JVM虚拟机

  • JVM平台上还可以运行其他语言,运行的是Class字节码。只要能翻译成Class的语言就OK了。挺强大的。
  • JVM厂商很多
  • 垃圾收集器、收集算法
  • JVM检测工具

 

关于类的加载:

  •  Java代码中,类型(interface, class,enum等,有些是在运行时候生成的,比如动态代理)的加载、连接与初始化过程都是在程序运行期间完成的。不涉及到对象的概念。同时也是个Runtime阶段。
  •  提供了更大的灵活性,增加了更多的可能性。提供了一些扩展,灵活扩展。

    

Java虚拟机与程序的生命周期:

  在如下几种情况下,Java虚拟机将会结束生命周期:

  1. 执行了System.exit()方法
  2. 程序正常执行结束
  3. 程序执行过程遇到了异常或者错误异常终止了
  4. 操作系统出现错误导致Java虚拟机进行终止

 

类的加载、连接与初始化:

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

连接: 

  • 验证: 确保被加载类的正确性。Class有格式的。
  • 准备:为类的静态变量分配内存,并将其初始化为默认值  
  • 注:
    1.类的静态变量或类的静态方法,通常可以看做全局的,由类去直接调用。此时还是个类的概念,不存在对象。
    2.关于默认值问题:
    class Test{
    public static int a = 1;
    }
    中间过程: Test类加载到内存的过程中,会给a分配一个内存。然后将a初始化为默认值0(整型变量)

  • 解析: 把类中的符号引用转为直接引用。符号的引用也是间接的引用方式。

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

  • class Test{
       public static int a = 1;
      }
    此时的a才真正成为1了
    

      

类的使用与卸载

 使用: 类的方法变量使用等

 卸载: class字节码文件,加载到内存里面。形成了自己的数据结构,驻留在内存里面。可以销毁掉。卸载到了就不能进行new 对象了。

 

总体流程:

深入理解JVM虚拟机_第1张图片

 

 

 

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

  1. 主动使用
  2. 被动使用

 

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

 

主动使用,七种(非精确划分,大体划分):

  1. 创建类的实例。
  2. 访问某个类或接口的静态变量,或者对静态变量赋值。 字节码层面上,使用的助记符:get static、  put static
  3. 调用类的静态方法。 invoke static
  4. 反射(如Class.forName("com.test.t1"))
  5. 初始化一个类的子类
    比如:
    
     class Parent{}
     class Child extends Parent{}
    
    初始化Child时候,先去初始化Parent 
  6. Java虚拟机启动时被表明为敌情类的类(Java Test)
    Java虚拟机启动时候,被标明为启动的类,即为有main方法的类,也会主动使用 
  7. JDK1.7开始提供动态语言支持:
    注:
    1.java.lang.invoke.MethodHandle实例的解析结果REF_getStatic, REF_putStatic, REF_invokeStatic句柄对应的类没有初始化,则初始化
    2.1.7开始提供了对动态语言的支持。特别的JVM平台上通过脚本引擎调用JS代码(动态语言)。  

:助记符了解即可

 

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

 

类的加载:

 类的加载指的是将类 .class文件中的二进制数据读入内存中,将其放在运行时数据区的方法区内,然后在内存中创建一个java.lang.Class对象(规范并说明Class对象位于哪里,HotSpot虚拟机将其放在了方法区中,JVM没有规范这个)用来封装类在方法区内的数据结构

 引申:一个类不管生成了多少实例,所有的实例对应只有一份Class对象。 Class对象是面镜子,能反映到方法区中的Class文件的内容、结构等各种信息。

加载.class文件的方式:

  1. 从本地系统中直接加载
  2. 通过网络下载
  3. 从zip、jar等贵方文件中加载
  4. 从转悠数据库中提取
  5. 将Java源文件动态编译为.class文件

 

public class MyTest1 {
    public static void main(String[] args) {
        System.out.println(MyChild1.str1);
//        System.out.println(MyChild1.str2);
    }
}

class MyParent1{

    //静态成员变量
    public static String str1 = "str1";
    // 静态代码块(程序加载初始化时候去执行)
    static {
        System.out.println("MyParent1 -----> static block running");
    }
}
class MyChild1 extends MyParent1{
    //静态成员变量
    public static String str2 = "str2";
    static {
        System.out.println("MyChild1 -----> static block running");
    }
}

  

 

 

 

str1 子类调用了继承到的父类的str1,子类的静态代码块没有执行。str1是父类中定义的。MyParent1的主动使用,但是没有主动使用MyChild1. 总结:看定义的!

 

 

str2 可以执行,同时初始化子类时候,父类会主动使用。所有的父类都会被初始化

深入理解JVM虚拟机_第2张图片

 

 

总结: 

  1. 对于静态字段来说,只有直接定义了该字段的类才会被初始化。
  2. 当一个类在初始化时候,要求其父类全部已经初始化完毕。每个父类最多只能初始化一次! 

 

你可能感兴趣的:(深入理解JVM虚拟机)