字节码文件、类加载机制、

导读:本篇文章讲解 字节码文件、类加载机制、,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

Java字节码文件:

在这里插入图片描述

计算机是不能直接运行java代码的,必须要先运行java虚拟机,再由java虚拟机运行编译后的java代码。

  • Java代码间接翻译成字节码,储存字节码的文件再交由运行于不同平台上的JVM虚拟机去读取执行,从而实现一次编写,到处运行的目的。
  • JVM也不再只支持Java,由此衍生出了许多基于JVM的编程语言,如Groovy, Scala, Koltin等等。

在这里插入图片描述
Class文件的结构属性

  • 常量池
  • 访问标志
  • 类和接口的引用
  • 字段表属性
  • 方法表属性
  • 属性表属性
    在这里插入图片描述

类加载机制

一、类的生命周期:

其中类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。
在这里插入图片描述
1:解析阶段不一定按照顺序发生,其余的时按照顺序发生的;
2:这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。
加载:加载过程就是把class字节码文件载入到虚拟机中,至于从哪儿加载,虚拟机设计者并没有限定,你可以从文件、压缩包、网络、数据库等等地方加载class字节码。

连接

验证确保被类加载的正确性
是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成4个阶段的检验动作:
1:文件格式验证;
2:元数据验证;
3:字节码验证;
4:符号引用验证;

准备:为类的静态变量分配内存,并将其初始化默认值;
准备阶段的工作就是为类的静态变量分配内存并设为jvm默认的初值,对于非静态的变量,则不会为它们分配内存。静态变量的初值为jvm默认的初值,而不是我们在程序中设定的初值。(仅包含类变量,不包含实例变量).

解析:把类中的符号应用转换为直接引用
虚拟机将常量池中的符号引用替换为直接引用,解析动作主要针对类或接口,字段,类方法,方法类型等等。

初始化
使用
卸载

二、类的加载机制:

2.1、加载阶段,JVM虚拟机要做的事情:

  • 通过一个类的全限定名来获取其定义的二进制字节流。
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  • 在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。

在这里插入图片描述
2.2、类加载的层次

带着问题解答:
多个对象是同一个吗,他们的反射对象那;类加载器有哪些?

下面解答:代码实现

2.3、寻找类加载器


package com.baidu.JVM;

/**
 * 1:类加载器收到类加载的请求;
 * 2:将这个请求委托给父类加载器去完成,一直向上委托,直到启动类加载器;
 * 3:启动类加载器检查是否能够加载当前类,能加在就结束,使用当前加载类,否则,抛出异常,通知子加载器加进行载
 * 4:重复步骤 3
 *  * null  java 调用不到,Java 底层是C  C++;
 */
public class Car {

    public static void main(String[] args) throws ClassNotFoundException {

        //这里通过创建三个不同的对象的hash 值肯定是不一样的,但是通过反射级制获得的 Class 类的模板只有一个;
        Car car1 = new Car();
        Car car2 = new Car();
        Car car3 = new Car();

        System.out.println(car1.hashCode());
        System.out.println(car2.hashCode());
        System.out.println(car3.hashCode());

        Class<? extends Car> aClass1 = car1.getClass();
        Class<? extends Car> aClass2 = car1.getClass();
        Class<? extends Car> aClass3 = car1.getClass();

        System.out.println(aClass1.hashCode());
        System.out.println(aClass2.hashCode());
        System.out.println(aClass3.hashCode());

        ClassLoader classLoader = aClass1.getClassLoader();

        System.out.println(classLoader);  //$AppClassLoader
        System.out.println(classLoader.getParent()); //$ExtClassLoader
        //原因是BootstrapLoader(引导类加载器)是用C语言实现的,找不到一个确定的返回父Loader的方式,于是就返回null
        System.out.println(classLoader.getParent().getParent());  //null  1:不存在  2:java程序获取不到

    }
}

测试结果:
在这里插入图片描述
在这里插入图片描述

  • 启动类加载器 Bootstrap ClassLoader
  • 扩展类加载器 Extension ClassLoader
  • 应用程序加载器 Application ClassLoader
  • 自定义加载器

2.4、JVM类加载机制

  • 全盘负责
    当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入

  • 父类委托
    先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类

  • 缓存机制
    缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效

  • 双亲委派机制
    如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
    在这里插入图片描述
    所以上述类是这么加载的

    • Java自带的核心类 – 由启动类加载器加载
    • Java支持的可扩展类 – 由扩展类加载器加载
    • 我们自己编写的类 – 默认由应用程序类加载器或其子类加载

好处:
这种设计有个好处是,如果有人想替换系统级别的类:String.java。篡改它的实现,在这种机制下这些系统的类已经被Bootstrap classLoader加载过了(为什么?因为当一个类需要加载的时候,最先去尝试加载的就是BootstrapClassLoader),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。

  • 系统类防止内存中出现多份同样的字节码
  • 保证Java程序安全稳定运行

https://blog.csdn.net/codeyanbao/article/details/82875064
https://www.pdai.tech/md/java/jvm/java-jvm-class.html
https://www.pdai.tech/md/java/jvm/java-jvm-classload.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/71862.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!