当前位置: 首页 > news >正文

JVM 调优篇1 类的加载器与加载过程

一  基本知识

1.1 JIT&AOT

JIT:  Just Time compilation  即时编译器

在程序运行时将字节码或中间表示转换为机器代码。

AOT: Ahead of  Tmie  Compilation : 预编译

在程序运行之前将高级语言代码完全编译成机器代码。

1.2 字面量和符号引用*

字面量:在java中,字面量是指在代码中直接出现具体的值,如 String str=“123”;

符号引用:是对某个方法、字段或类的引用,这个引用指向元数据的引用。

public class Jia
{int field;void method(){}public static void main(String[] args) {Jia j = new Jia();j.field = 33;//字段引用j.method();//方法引用}
}

field是对类的字段的符号引用,method()是对类的方法的符号引用。这些引用指向的是元数据,而不是具体的值。

二  类的加载

2.1 jvm的类加载器*

ClassLoader是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过各种方式将Class信息的二进制数据流读入JVM内部,转换为一个与目标类对应的java.lang.Class对象实例。然后交给Java虚拟机进行链接、初始化等操作。

类加载器分类:

1)启动类加载器(引导类加载器,Bootstrap ClassLoader)

这个类加载使用C/C++语言实现的,嵌套在JVM内部。它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar或sun.boot.class.path路径下的内容)。用于提供JVM自身需要的类。

并不继承自java.lang.ClassLoader,没有父加载器。

出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类。加载扩展类和应用程序类加载器,并指定为它们的父类加载器。

 2)扩展类加载器(Extension ClassLoader)

Java语言编写,由sun.misc.Launcher$ExtClassLoader实现。

继承于ClassLoader类;父类加载器为启动类加载器

从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载。

3)应用程序类加载器(系统类加载器,AppClassLoader)

java语言编写,由sun.misc.Launcher$AppClassLoader实现

继承于ClassLoader类;父类加载器为扩展类加载器

它负责加载环境变量classpath或系统属性java.class.path 指定路径下的类库 

应用程序中的类加载器默认是系统类加载器。它是用户自定义类加载器的默认父加载器

通过ClassLoader的getSystemClassLoader()方法可以获取到该类加载器

4)用户自定义类加载器

自定义类加载器体现Java语言强大生命力和巨大魅力的关键因素之一便是:Java开发者可以自定义类加载器来实现类库的动态加载,加载源可以是本地的JAR包,也可以是网络上的远程资源。

所有用户自定义类加载器通常需要继承于抽象类java.lang.ClassLoader。

通过类加载器可以实现非常绝妙的插件机制

自定义加载器能够实现应用隔离

5)获取类的加载器

2.2 Class.forName与Class.getClassLoader()的区别与联系?

Class.forName():是一个静态方法,最常用的是Class.forName(String className);根据传入类的全路径包名,返回一个 Class 对象。该方法在将 Class文件加载到内存的同时,会执行类的初始化。

如: Class.forName("com.atguigu.java.HelloWorld");

ClassLoader.loadClass():这是一个实例方法,需要一个 ClassLoader 对象来调用该方法。该方法将 Class 文件加载到内存时,并不会执行类的初始化,直到这个类第一次使用时才进行初始化。该方法因为需要得到一个 ClassLoader 对象,所以可以根据需要指定使用哪个类加载器。

如:ClassLoader cl=.......;    cl.loadClass("com.atguigu.java.HelloWorld");

String.class.getClassLoader().loadClass() 与Class.forName()的区别_.class.getclassloader();会调用loadclass方法吗-CSDN博客

2.3 类的双亲委托策略

类加载器用来把类加载到Java虚拟机中,类的加载过程采用双亲委派机制,这种机制能更好地保证Java平台的安全。

如果一个类加载器在接到加载类的请求时,它首先不会自己尝试去加载这个类,而是把这个请求任务委托给父类加载器去完成,依次递归,如果父类加载器可以完成类加载任务,就成功返回。只有父类加载器无法完成此加载任务时,才自己去加载。

优点:

避免类的重复加载,确保一个类的全局唯一性

Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关系可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。

保护程序安全,防止核心API被随意篡改

缺点:

检查类是否加载的委托过程是单向的,这个方式虽然从结构上说比较清晰,使各个ClassLoader的职责非常明确,但是同时会带来一个问题,即顶层的ClassLoader无法访问底层的ClassLoader所加载的类。

2.4 编译后不包含<clinit>方法

java编译器在以下情况,经过编译字节码后,文件不包含<clinit>方法

1)一个类中并没有声明任何的类变量,也没有静态代码块时。

2)一个类中声明类变量,但是没有明确使用类变量的初始化语句以及静态代码块来执行初始化操作时

3)一个类中包含static final修饰的基本数据类型的字段,这些类字段初始化语句采用编译时常量表达式。

如截图所示:

2.5 初始化阶段与链接-准备阶段

 static+final修饰的成员变量,为全局常量;给全局常量的赋值为字面量或常量,不涉及到方法和构造器的调用,则是链接阶段的准备环节;否则是初始化阶段赋值。 

public class TestM

{

    public static int a = 1;//初始化阶段

    public static final int INT_CONSTANT = 10;//链接阶段

    public static Integer INTEGER_CONSTANT1 = Integer.valueOf(100);//初始化阶段

    public static final Integer INTEGER_CONSTANT2 = Integer.valueOf(1000);//初始化阶段

    public static final String s0 = "hello";//链接阶段

    public static final String s1 = new String("hello");//初始化阶段

    public static String  s2 = "hello";//初始化阶段

    public static final int NUM = new Random().nextInt(10);//初始化阶段

    static int a = 10; //初始化阶段

    static final  int b = a ;//初始化阶段

    //static+final修饰的成员变量,为全局常量;给全局常量的赋值为字面量或常量,不涉及到方法和构造器的调用,

    // 则是链接阶段的准备环节;否则是初始化阶段赋值。

}

三 类的加载过程

3.1 概述

按照Java虚拟机规范,从class文件到加载到内存中的类,到类卸载出内存为止,它的整个生命周期包括如下7个阶段:在Java中数据类型分为基本数据类型和引用数据类型。基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载。

3.2 加载具体阶段*

1加载:

所谓装载,简而言之就是将Java类的字节码文件加载到机器内存中,并在内存中构建出Java类的原型——类模板对象。

2.链接:

1)验证:当类加载到系统后,就开始链接操作,验证是链接操作的第一步。它的目的是保证加载的字节码是合法、合理并符合规范的。验证的内容则涵盖了类数据信息的格式验证、语义检查、字节码验证,以及符号引用验证等。

2)准备:

简言之,为类的静态变量分配内存,并将其初始化为默认值

在这个阶段,虚拟机就会为这个类分配相应的内存空间,并设置默认初始值。

注意:Java并不支持boolean类型,对于boolean类型,内部实现是int,由于int的默认值是0,故对应的boolean的默认值就是false。

3)解析:

将类、接口、字段和方法的符号引用转为直接引用;也就是得到类、字段、方法在内存中的指针或者偏移量。

3.初始化执行类变量赋值和静态代码块

又父及子,静态先行。在加载一个类之前,虚拟机总是会试图加载该类的父类,因此父类的<clinit>总是在子类<clinit>之前被调用。也就是说,父类的static块优先级高于子类。 

4.使用阶段:

任何一个类型在使用之前都必须经历过完整的加载、链接和初始化3个类加载步骤。一旦一个类型成功经历过这3个步骤之后,开发人员可以在程序中访问和调用它的静态类成员信息(比如:静态字段、静态方法),或者使用new关键字为其创建对象实例。

5.卸载

当对象不再被使用时,java虚拟机的垃圾收集器将会回收堆中的对象,方法区中不再被使用的Class也要被卸载,否则方法区(Sun HotSpot永久代)会内存溢出。

相关文章:

  • 老古董Lisp实用主义入门教程(8):挠痒痒先生建网站记
  • C#通过ACE OLEDB驱动程序访问 Access和 Excel
  • 逻辑代数的基本规则
  • (Java入门)学生管理系统
  • 记忆化搜索【下】
  • 【论文阅读】CiteTracker: Correlating Image and Text for Visual Tracking
  • 输送线相机拍照信号触发(博途PLC高速计数器中断立即输出应用)
  • 解决npm i 安装报npm ERR! code E401
  • 2024年AMC10美国数学竞赛倒计时两个月:吃透1250道真题和知识点(持续)
  • mybatis框架基础以及自定义插件开发
  • 极米科技:走出舒适圈,推动数据架构现代化升级 | OceanBase 《DB大咖说》
  • JavaScript 根据关键字匹配数组项
  • 算法练习题17——leetcode54螺旋矩阵
  • Go语言设计与实现 学习笔记 第六章 并发编程(3)
  • python基础语法十一-赋值、浅拷贝、深拷贝
  • @jsonView过滤属性
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • angular2开源库收集
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • Babel配置的不完全指南
  • CAP 一致性协议及应用解析
  • create-react-app项目添加less配置
  • CSS实用技巧
  • export和import的用法总结
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • js作用域和this的理解
  • Mac转Windows的拯救指南
  • Magento 1.x 中文订单打印乱码
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Sequelize 中文文档 v4 - Getting started - 入门
  • socket.io+express实现聊天室的思考(三)
  • spring-boot List转Page
  • Travix是如何部署应用程序到Kubernetes上的
  • vue-router的history模式发布配置
  • Vue学习第二天
  • 对象管理器(defineProperty)学习笔记
  • 前端
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 前端存储 - localStorage
  • 如何优雅地使用 Sublime Text
  • 使用Envoy 作Sidecar Proxy的微服务模式-4.Prometheus的指标收集
  • 算法-图和图算法
  • 想写好前端,先练好内功
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • # Redis 入门到精通(一)数据类型(4)
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (70min)字节暑假实习二面(已挂)
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (Java入门)抽象类,接口,内部类
  • (理论篇)httpmoudle和httphandler一览
  • (十八)SpringBoot之发送QQ邮件
  • (实测可用)(3)Git的使用——RT Thread Stdio添加的软件包,github与gitee冲突造成无法上传文件到gitee
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))