热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JVM笔记(一)类加载子系统

简介jvm是一个应用程序,其作用是运行jvm字节码,为什么叫jvm字节码呢?因为jvm支持运行各种语言编译成的字节码,而不仅仅是java,当然java字节码是最广泛的。其功能主要分




简介

jvm是一个应用程序,其作用是运行jvm字节码,为什么叫jvm字节码呢?因为jvm支持运行各种语言编译成的字节码,而不仅仅是java,当然java字节码是最广泛的。其功能主要分为两点:


  1. write once,run anywhere:在不同的硬件平台都有对应的jvm程序,字节码可以运行在任意平台的jvm中。
  2. 提供内存管理和垃圾回收等功能

JVM支持的指令流是基于栈的指令集架构,下面是将该方法所在类编译成的class文件,通过javap进行反编译后的代码,可以看到基本指令就是iconst,istore,iload等,另一种指令集架构是基于寄存器的指令集架构,代表就是汇编语言。来说说这两者的区别以及为什么JVM采用的基于栈的指令集架构。

public static void main(String[] args) {
int a = 1;
int b = 2;
int c = a + b;
}
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: istore_3
8: return

基于栈式架构


  1. 设计和实现简单,适用于资源受限的系统
  2. 指令集中的指令基本是零地址指令,指令集更小,编译器容易实现
  3. 不需要硬件支持,可移植性更好,更好实现跨平台

基于寄存器架构


  1. 依赖硬件,可移植性差
  2. 性能优秀,执行高效
  3. 花费更少的指令完成一项操作,以1,2,3地址指令 为主。

为了更好的跨平台,Java的指令都是根据栈来设计


发展历程

Sun Classic VM : Java1.0发布,世界上第一款商用Java虚拟机,只提供解释器

Exact VM : jdk1.2时发布,提供了准确式内存管理,热点探测及编译器与解释器混合工作模式,短暂使用,很快就被HotSpot替换

HotSpot:jdk1.3时发布,目前JDK6,8等新版本默认虚拟机都是HotSpot,HotSpot含义就是热点代码探测技术。

JRockit:BEA公司发明,已被oracle收购,专注于服务器端应用,不包含解释器,是世界上最快的JVM。JRockit Real Time(毫秒或微妙级响应)&MissionControl(极低开销来监控,管理和分析生产环境的工具)是其特点。

J9:IBM发布,仅广泛应用于IBM的各种Java产品,移植性较差。

KVM,CDC/CLDC HotSpot:Oracle应用在移动领域的Java虚拟机,已几乎不用;Azul VM:应用在Azul Systems的专有硬件Vega系统上。Liquid VM:运用在BEA公司Hypervisor系统,实现了操作系统的必要功能,可直接与硬件交互,目前已停止。Apache Harmony:IBM和Intel联合开发,但Sun公司不让其获得JCP认证,最终退役。最后被应用在了Android SDK中。MicroSoft JVM:微软用以支持浏览器中的Java程序,只能在windows平台运行,但遭Sun公司指控,被迫抹掉。目前windows上都是HotSpot。

Taobao JVM:阿里基于OpenJDK,深度定制且开源的高性能服务器版Java虚拟机。GCIH技术实现了off-heap:将生命周期长的Java对象从heap中移到了heap之外,且GC不管理此类对象,降低回收频率和提高回收效率,heap之外的对象还能在多个Java虚拟机进程中实现共享。降低JNI开销…


类加载子系统(Classload sub system)

类加载子系统负责从文件系统或者网络中加载Class文件,加载的类信息会存放在方法区的内存空间,除了类的信息外,方法区中还存放运行时常量信息,可能还包括字符串字面量和数字常量。分为三步:加载,链接,初始化。


加载(Loading)

简单来说是指查找字节流,并据此创建类的过程。详细:通过类的全限定名(路径和文件名)获取此类的二进制流,将二进制字节流的静态存储结构转化为方法区的运行时数据结构,在内存中生成一个java.lang.Class对象,作为这个类的数据的访问入口。

上述过程通过**类加载器(ClassLoader)**完成。类加载器分为启动类加载器(bootstrap),扩展类加载器(extention),应用类加载器(application)和自定义加载器。

启动类加载:由C++实现,在Java中用null来指代,只加载最基础最重要的类,例如JRE的lib目录下jar包中的类以及有虚拟机参数-Xbootclasspath指定的类。

扩展类加载器:其父类是启动类加载器,加载相对次要但通用的类,例如JRE的lib/ext目录下jar包的类,以及java.ext.dirs指定的类。(java 9 之后,改名为平台类加载器,加载更多类)

应用类加载器:其父类是扩展类加载器,加载应用程序路径下的类。

自定义加载器:实现特殊的加载方式,例如对class文件加密,加载时利用自定义的类加载器进行解密后加载。

双亲委派模型

JVM对class文件采用按需加载的方式,加载class文件时采用的双亲委派模型,如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WB59LCZR-1609912271538)(C:\Users\z672916\Desktop\日常文档\pic\类加载.webp)]

当收到类加载请求,会先查找是否加载过,若加载过,直接返回,反之不会自己先去加载,而是把这个请求委托给父类加载器,若父类加载器还存在父类加载器,进一步向上委托,最终可能会到顶层的启动类加载器进行加载。若父类加载器可以完成类加载,则成功返回,若无法完成,则子加载器会尝试加载。看下源码就很清晰了。

protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先检查这个classsh是否已经加载过了
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// c==null表示没有加载,如果有父类的加载器则让父类加载器加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//如果父类的加载器为空 则说明递归到bootStrapClassloader了
//bootStrapClassloader比较特殊无法通过get获取
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {}
if (c == null) {
//如果bootstrapClassLoader 仍然没有加载过,则递归回来,尝试自己去加载class
long t1 = System.nanoTime();
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

优点:假设攻击者想篡改系统级别的类,例如String.class,但是呢启动类加载器已经加载了String.class对象,便不会再加载篡改后的,保证了一定的安全性;避免重复加载,父加载器加载过的class对象,不会再次加载。

PS:在JVM虚拟机中,类的唯一性由类加载器实例和类的全名一同确定,哪怕相同的class文件,由不同的类加载器加载,也会得到两个不同的类。


链接(LINKING)

链接分为三个步骤,分别是验证,准备,解析


验证(Verify)

确保class文件的字节流中包含的信息符合虚拟机要求,保证被加载类的正确性,不会危害虚拟机。包含四种验证:文件格式验证(所有class文件的开头的都是cafe babe,叫做魔数),元数据验证,字节码验证,符号引用验证。


准备(prepare)

为类变量(以static修饰的变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。


  1. 不会为实例变量分配初始化,而实例变量会随着对象分配在堆中
  2. 设置初始值,会设为默认的零值

为类静态变量分配内存并且设置该类变量的默认初始值,即零值。不包含用static final 修饰的常量,static final在编译时候就分配了。不会为实例变量分配初始化,类静态变量会分配方法区中,而实例变量会随着对象分配在堆中。


解析(Resolve)

将常量池(指的是栈帧中的常量池)内的符号引用转换为直接引用。

解析操作往往伴随JVM执行完初始化之后再执行


初始化(initialize)

执行()方法的过程。

1.此方法是javac编译器自动收集类中所有类变量的赋值操作和静态代码块中的语句合并而来。()方法中的指令按照语句在原文件中出现的顺序执行。不同于构造器方法()方法(类加载完成后,创建对象时候将调用的 ()方法来初始化对象)

public class Test {
static {
// 给变量赋值可以正常编译通过
i = 0;
// 这句编译器会提示"非法向前引用"
System.out.println(i);
}
static int i = 1;
}

2.若该类包含父类,JVM会保证再执行子类()方法前,先调用父类的()方法。

3.JVM会保证在多线程下一个类的()方法同步加锁。


总结

JVM是一个基于栈的指令集架构,运行字节码的应用程序,并且提供了内存管理,垃圾回收等功能。类加载子系统是JVM运行字节码的第一步,将编译后的class文件加载到内存中。其完成流程如下:
在这里插入图片描述



推荐阅读
  • 本文总结了初学者在使用dubbo设计架构过程中遇到的问题,并提供了相应的解决方法。问题包括传输字节流限制、分布式事务、序列化、多点部署、zk端口冲突、服务失败请求3次机制以及启动时检查。通过解决这些问题,初学者能够更好地理解和应用dubbo设计架构。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • 嵌入式处理器的架构与内核发展历程
    本文主要介绍了嵌入式处理器的架构与内核发展历程,包括不同架构的指令集的变化,以及内核的流水线和结构。通过对ARM架构的分析,可以更好地理解嵌入式处理器的架构与内核的关系。 ... [详细]
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
  • Java如何导入和导出Excel文件的方法和步骤详解
    本文详细介绍了在SpringBoot中使用Java导入和导出Excel文件的方法和步骤,包括添加操作Excel的依赖、自定义注解等。文章还提供了示例代码,并将代码上传至GitHub供访问。 ... [详细]
  • 本文介绍了在RHEL 7中的系统日志管理和网络管理。系统日志管理包括rsyslog和systemd-journal两种日志服务,分别介绍了它们的特点、配置文件和日志查询方式。网络管理主要介绍了使用nmcli命令查看和配置网络接口的方法,包括查看网卡信息、添加、修改和删除配置文件等操作。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
  • 突破MIUI14限制,自定义胶囊图标、大图标样式,支持任意APP
    本文介绍了如何突破MIUI14的限制,实现自定义胶囊图标和大图标样式,并支持任意APP。需要一定的动手能力和主题设计师账号权限或者会主题pojie。详细步骤包括应用包名获取、素材制作和封包获取等。 ... [详细]
author-avatar
Christy-1221
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有