zoukankan      html  css  js  c++  java
  • 虚拟机类加载机制

    一、JVM的生命周期

    虚拟机的声明周期可以分为三个阶段:

    1. 虚拟机的启动

      虚拟机的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现来指定的。

    2. 虚拟机的执行

      ​ 一个运行中的Java虚拟机有这一个清晰的任务就是执行Java程序,程序开始执行时虚拟机,当程序运行结束时,虚拟机就结束,在执行一个Java程序的时候,真真正正执行的是一个叫做Java虚拟机的进程。

    3. 虚拟机的退出

      虚拟机的退出又分为几种情况:

      • 程序正常执行结束
      • 程序运行过程中遇到异常而终止
      • 操作系统出现错误而导致Java虚拟机进程终止
      • 某个线程调用Runtime类或System类的exit方法,或者Runtime类的halt方法,并且Java安全管理器也允许这次exit或者halt操作。
      • 还有一种就是JNI(Java Native Interface)规范描述了用JNI Invocation API来加载或卸载Java虚拟机时,Java虚拟机退出的情况。

    二、常见的Java虚拟机

    • Classic VM:,它是世界上第一款商用虚拟机。这款虚拟机只提供解释器并没有即时编译器。
    • Exact VM:sun提供的又一款虚拟机Exact Memory Management,准确式内存管理。
    • HotSpot VM:大多数的jdk的默认虚拟机,也是Oracle JDK和Open JDK的默认虚拟机,其拥有解释器和即时编译器,在1.8的jdk中HotSpot中整合了JRockit虚拟机的优秀特性。
    • JRockit:BEA的JRockit虚拟机,号称世界上最快的虚拟机,2008年BEA被Oracle收购。
    • J9:IBM公司的J9虚拟机,是目前最右影响力的三大商用服务器之一。2017年,IBM发布了开源J9VM命名为OpenJ9,交给了Ecilpse基金会,也称为Ecilpase OpenJ9。
    • TaobaoJVM:由AliJVM团队发布,是阿里巴巴基于OpenJDK开的自己的定制版本AlibabaJDk,它是国内第一个优化,深度定制且开源的高性能服务器。目前已经在淘宝天猫上线

    三、虚拟机的类加载机制

    3.1、概述

    ​ Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称为虚拟机的类加载机制,在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的。

    3.2、类加载器的作用和角色

    3.2.1 类加载器的作用

    类加载器的作用就是将类class文件加载到内存中,对方法区中的类信息的访问都需要经过这个Class对象

    image-20201006210716195

    3.2.1类加载器所扮演的角色

    image-20201006211339225

    3.3、类加载过程

    类的加载过程分为五个阶段

    image-20201006212122660

    3.3.1、加载过程

    image-20201006212151731

    image-20201006212211611

    类加载后会在堆区生成一个唯一的Class对象。

    3.3.2、链接过程

    image-20201006212632790

    在准备阶段如果变量是final static修饰的常量则不会赋予零值,而是会直接赋值如:以下代码会被直接赋值为123;

    public static int value = 123
    

    3.3.3、初始化过程

    image-20201006212830197

    类构造器是收集静态代码和静态变量形成的,如果没有静态变量和静态代码块则并不会有类构造器,初始化过程就是执行类构造器的过程。

    注意:执行接口的类加载器并不需要先执行父接口的类构造器,因为只有当父接口中定义的变量被使用时,父接口才会被初始化,此外,接口的实现类在初始化时也一样不会执行接口的类构造器

    3.4、类加载的时机

    类的加载过程只有五个阶段但是类的生命周期会经历,加载,验证,准备,解析,初始化,使用和卸载七个阶段,其中验证,准备,解析统称为链接。

    对类的主动使用会触发类的初始化

    java程序对类的主动引用:

    image-20201006214631495

    添加一点:深入理解Java虚拟机第三版中提到当一个接口中定义了jdk8新加入的默认方法(被default关键字修饰的接口方法)时,如果这个接口的实现类发生了初始化,那改接口要在其之前被初始化


    对类的别动引用不会触发类的初始化

    java程序对类的被动引用:

    • 通过子类引用父类的静态字段不会导致子类的初始化
    • 通过数组定义来引用类,不会触发此类的初始化
    • 常量在编译器会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化

    四、类加载器的分类

    image-20201006220112319

    • 启动类加载器

    image-20201006220422636

    • 扩展类加载器

    image-20201006220443013

    在jdk9中,扩展类加载器被平台类加载器所取代

    • 应用程序类加载器

    image-20201006220457569

    image-20201006220144771


    用户自定义的加载器

    为什么要自定义类加载器:

    image-20201006220621262

    如何自定义类加载器:

    image-20201006220651161

    自定义的类加载器需要继承ClassLoader,有关ClassLoader

    image-20201006220748642

    image-20201006220801918

    如何获取类加载器:

    image-20201006220833709

    通过方式一获取类加载器,如果获取到的结果为null,则表示是一个引导类加载器

    例如:

    //	shareData是自定义类  ,String类是由引导类加载器加载的
    public static void getClassLoader(){
            String s = new String();
            System.out.println("类加载器"+s.getClass().getClassLoader());
            ShareData shareData = new ShareData();
            System.out.println("类加载器"+shareData.getClass().getClassLoader());
        }
    

    image-20201006222112848

    注意:判断两个class对象是否是同一个类要注意两点:

    • 类的全限定类型是否相等
    • 加载类的类加载器是否相同

    image-20201006221047149

    image-20201006221059046

    五、双亲委派模型

    image-20201006221206457

    image-20201006221218357

    image-20201006221358244

    image-20201006221411801

    沙箱安全机制

    image-20201006221432965
    双亲委派机制可以保护核心类不被篡改:

    自定义一个java.lang.String类,在main方法中会报错

    package java.lang;
    
    /**
     * @author mypc
     * @create 2020 上午 11:40
     */
    public class String {
        //
        static{
            System.out.println("我是自定义的String类的静态代码块");
        }
        public static void main(String[] args) {
            System.out.println("hello,String");
        }
    }
    

    image-20201006225154286

    即使这个类是自定义的,理应由应用程序类加载器加载,但是由于双亲委派机制的存在,它会首先将该类交给它的父类加载器扩展类加载器加载,扩展类加载器又会交给它的父类加载器引导类加载器加载,而java.lang.String这个类在jdk的核心类库中存在,所以它会直接加载核心类库中的java.lang.String,不会加载我们自定义的java.lang.String,这样就防止了核心API被随意串改。java的核心类java.lang.String没有main方法,所以报错.
    以上内容是根据尚硅谷的视屏和深入理解Java虚拟机第三版整理的笔记,如有错误请指出

  • 相关阅读:
    php常用函数总结
    PHP常用函数(收集)
    Web开发者的最爱 5个超实用的HTML5 API
    打开MySQL数据库远程访问的权限
    centos yum 安装问题
    CentOS6.4安装VNC
    删:Centos 7安装Nginx 1.8
    centos6.3安装nginx
    MySQL5.7重置root密码
    CentOS下MySQL忘记root密码解决方法【转载】
  • 原文地址:https://www.cnblogs.com/myblogstart/p/13775895.html
Copyright © 2011-2022 走看看