【快速入门】JVM之类加载机制与Native

news2024/10/2 20:33:08

感慨:
如何定义一个合格的Java程序员,Java程序员要了解掌握哪些知识点,网上的面试题太多了,后端需要了解掌握的知识点太多太多了,Java基础、数据结构、异常、多线程、Spring、Spring boot、事务、算法、数据库(Oracle、MySQL等)、缓存、中间件(各种类型的)、并发异步、消息中间件、微服务、netty(最起码要知道有这个东西吧)、大数据相关(Hive、spark、flink等)、JVM、网络、日志等等等等,太多太多了,每一个单独拿出来都有太多的点了。
所以说,后端Java开发并不是单纯的学习框架+编写业务逻辑,这样简直太简单了,也就是太容易被替代了。
最近问身边很多工作多年的程序员关于jvm相关的知识,基本没有人答得出来,甚至是一些比较浅显的概念点,都说了解这个有什么用呢?实际开发是没有用,但是我还是觉得要深入思考,要考虑底层原理,知其然且知其所以然。
所以一直想写一个关于JVM的文章,不用太复杂但是可以把一些基本的知识概念概括一下,但是jvm的点太多了,我们就说个大概吧,也可以理解为面试知识点向,所以标题是快速入门。

前言

本篇分为以下几个部分:类加载相关、Native相关、PC寄存器(概念)、堆、栈、GC相关

文章目录

  • 前言
  • 类加载
    • 类加载的过程
      • 加载
      • 验证
      • 准备
      • 解析
        • 符号引用与直接引用
      • 初始化
        • init和clinit区别
    • 类加载器
      • 三种类加载器
      • 双亲委派
      • 好处
  • Native
    • 概念
    • JNI

类加载

在Java中,每一个类(.java文件)再通过编译器后,都会形成一个.class文件。
类加载机制指的是将这些.class文件中的二进制数据读入到内存中,并对数据进行校验,解析和初始化。最终,每一个类都会在方法区保存一份它的元数据,在堆中创建一个与之对应的Class对象。

类加载的过程

类的加载过程分 5 个阶段:加载、验证、准备、解析、初始化,其中验证、准备、解析可以归纳为连接阶段。如图示:
在这里插入图片描述
虽然五个阶段之间有箭头指向,但是并不是严格的按照顺序完成,在类加载过程中,这些阶段会交叉混合执行以完成类的加载及初始化。

加载

加载主要是通过类加载器将.class文件读入内存的过程,主要是完成以下操作:

  • 通过类的全限定名定位到.class文件。
  • 读取.class文件的二进制数据,转化为方法区的运行时数据结构(方法区jdk8后由元空间实现,且不在jvm内存中了,而是直接放到了本地内存中,不受jvm参数限制,这个后面具体再讲)。
  • 在JVM堆内存Eden区(伊甸园区)中生成一个java.lang.Class对象,即程序中使用任何类时,系统都会为之建立一个java.lang.Class对象(Class模板。它是用来表示这个类的Class对象!一个类的实例对象可以有多个,但Class对象只有一个,一个类的class对象与其类的元数据一一对应),系统中所有的类都是java.lang.Class的实例。

验证

字面意思很好理解,就是对.class文件进行一系列的校验呗。目的是确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
包括(具体不展开了,稍微了解下吧):

  • 文件格式验证:验证字节流是否符合Class文件格式的规范。
  • 元数据验证:其实就是这个类对应.class文件的字节码描述的元数据信息是否符合Java规范。比如:是否存在父类,父类的继承链是否正确,抽象类是否实现了其父类或接口之中要求实现的所有方法,字段、方法是否与父类产生矛盾等。该校验更多是Java规范层面的校验。
  • 字节码验证:对类的方法体进行校验分析,确保这些方法在运行时是合法的、符合逻辑的。
  • 符号引用验证:发生在解析阶段。
    验证阶段不是必须执行的,该阶段可以跳过以缩短虚拟机类加载的时间。

准备

在该阶段会为类的静态字段信息(即被static修饰的变量)分配内存,并且设置初始值

  • 内存分配仅包括 static 修饰过的变量,而不包括实例变量,实例变量得等到对象实例化时分配内存。
  • 初始值指的是变量数据类型的默认值,而不是被在Java代码中被显式地赋予的值。但是,当字段信息被 final 修饰成常量时,这个初始值就是Java代码中显式地赋予的值。
  • 这些静态变量是存放在方法区内的,方法区是一个逻辑概念,其具体实现为jdk1.8的元空间与jdk1.8之前的永久代,JDK8以后取消了永久代改为元空间,而元空间在逻辑上存在物理上不存在,这些类变量的内存实际上是在堆内存中的。

解析

虚拟机会把这个Class文件中,常量池内的符号引用转换为直接引用。主要解析的是 类或接口、字段、类方法、接口方法、方法类型、方法句柄等符号引用。我们可以把解析阶段中,符号引用转换为直接引用的过程,理解为当前加载的这个类,和它所引用的类,正式进行“连接“的过程。

符号引用与直接引用

符号引用就是一个类中(当然不仅是类,还包括类的其他部分,比如方法,字段等),引入了其他的类,可是JVM并不知道引入的其他类在哪里,所以就用唯一符号来代替,等到类加载器去解析的时候,就把符号引用找到那个引用类的地址,这个地址也就是直接引用。

通俗的话来讲就是:

  • 符号引用其实可以理解为一个唯一标识,可以是任意值,只要能通过这个值能定位到目标。
  • 直接引用就是可以直接或间接指向目标内存位置的指针或句柄。

初始化

这个步骤是不是与bean生命周期的初始化阶段名称相同?但是其实是完全不同的处理阶段。类加载的初始化过程,就是执行类构造器 ()方法的过程。
类加载初始化完成后,类中static修饰的变量会赋予程序员实际定义的“值”,同时类中如果存在static代码块,也会执行这个静态代码块里面的代码。

init和clinit区别
  • 最根本的区别就是:init是实例构造器,对非静态变量解析初始化,而clinit是class类构造器对静态变量,静态代码块进行初始化。
  • () 方法由编译器自动生成,但不是必须生成的,只有这个类存在static修饰的变量,或者类中存在静态代码块但时候,才会自动生成()方法。它是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并产生的(不包括构造函数中的语句)。
  • 如下代码:
class Phli {
   static Log log = LogFactory.getLog(); // <clinit>
   private int x = 1;   // <init>
   X(){
      // <init>
   }
   static {
      // <clinit>
   }
}

类加载器

先看一张图:
在这里插入图片描述
从图中可以看出:

  • .class文件经过类加载器加载,其实也就是通过类全限定名来获取描述此类的二进制字节流的过程,而这正是类加载器在加载阶段要完成的动作。
  • 在堆中存放生成的Class类模板,后续就可以通过模板new不同的实例。
  • 类实例可以通过getClass获得Class模板,不同实例getClass获得的模板都是相同的。
  • Class模板可以通过getClassLoader获取其对应的类加载器。
  • 类加载器实现的功能,要完成的动作就是我们加载要完成的动作,在次不做赘述。

贴个main方法代码,印证下以上逻辑:

public class Phli {
    public String name;

    public static void main(String[] args) {
        Phli phli1 = new Phli();
        Phli phli2 = new Phli();
        Phli phli3 = new Phli();
        System.out.println(phli1.hashCode());
        System.out.println(phli2.hashCode());
        System.out.println(phli3.hashCode());

        Class<? extends Phli> aClass1 = phli1.getClass();
        Class<? extends Phli> aClass2 = phli2.getClass();
        Class<? extends Phli> aClass3 = phli3.getClass();
        System.out.println(aClass1.hashCode());
        System.out.println(aClass2.hashCode());
        System.out.println(aClass3.hashCode());
    }
}

执行结果:
在这里插入图片描述
很明显不同实例的Class模板是同一个。

三种类加载器

包括三类加载器(也可以说是四类,多了一个自定义加载器):启动类加载器(根加载器)、扩展类加载器及应用程序类加载器。这三种类加载器是分层的,类似于上下级关系(parent-child关系)。

  • 启动类加载器(Bootstrap):负责加载<JAVA_HOME>\lib 目录,或者被 -Xbootclasspath 参数制定的路径,例如 jre/lib/rt.jar 里所有的class文件。由C++实现,不是ClassLoader子类。
  • 扩展类加载器(Ext):负责加载Java平台中扩展功能的一些jar包,包括<JAVA_HOME>\lib\ext 目录中 或 java.ext.dirs 指定目录下的jar包。由Java代码实现。
  • 应用程序类加载器(App):负责加载ClassPath路径下所有jar包。

验证下,在刚才代码基础上在添加以下代码:

ClassLoader classLoader1 = aClass1.getClassLoader();
System.out.println(classLoader1);
ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2);
ClassLoader classLoader3 = classLoader2.getParent();
System.out.println(classLoader3);

执行结果:

sun.misc.Launcher$AppClassLoader@b4aac2
sun.misc.Launcher$ExtClassLoader@1615099
null

可以看到classLoader1为AppClassLoader也就是应用类加载器,然后调用它的getParent方法,得到的是ExtClassLoader也就是扩展类加载器,然后我们继续往上获取,结果为null。为什么为null,不应该是启动类加载器吗?
其实很简单,因为启动类加载器是C++实现的,Java程序获取不到~

双亲委派

在此之前我们先看一段代码:

package java.lang;

public class String {
    public void test() {

    }
    public static void main(String[] args) {
        String string = new String();
        System.out.println("main");
    }
}

我们定义了一个package为java.lang的String类,然后其中包括main方法,执行看下结果:
在这里插入图片描述
what??明明有main方法为什么错误信息提示找不到main方法呢?这是因为双亲委派导致的。
双亲委派其实很简单,下面一句话就可以解释清楚:

任何一个类加载器在接到一个类的加载请求时,都会先让其父类进行加载,只有父类无法加载(或者没有父类)的情况下,才尝试自己加载

为什么没找到main方法,因为应用程序类加载器向上让扩展类加载器进行加载,扩展类根据全限定名没加载到,继续往上让启动类加载器加载,启动类加载了我们rt.jar里面的String了,肯定是没有main方法的。
在这里插入图片描述
这就是双亲委派,总结一下:

  1. 类加载器收到类加载的请求;
  2. 将这个请求向上委托给父类加载器去加载,一直向上委托,直到启动类加载器(Bootstrap);
  3. 启动类加载器检查是否能够加载当前类,能加在就结束,使用当前加载器,否则抛出异常,通知子加载器进行加载。
  4. 重复步骤3。

好处

使用双亲委派最大的作用就是:安全,可以保证Java核心类的API不会被随意篡改。

Native

这个更多的是了解是干什么的,熟悉概念。

概念

native首先是个关键字,是一个计算机函数,一个Native Method就是一个Java调用非Java代码的接口。方法的实现由非Java语言实现,比如C或C++。好,概念问题了解到这儿就可以了。

凡是用到native关键字的,就说明Java的作用范围达不到了。
其实平时的业务开发基本用不到,但是它是无处不在的,举几个例子:

  • Object基类中的方法:
public final native Class<?> getClass();
public native int hashCode();
protected native Object clone() throws CloneNotSupportedException;
  • Thread.start()
    在这里插入图片描述

JNI

JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。 [1]
从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java
虚拟机环境。

通过 JNI,我们就可以通过 Java 程序(代码)调用到操作系统相关的技术实现的库函数,从而与其他技术和系统交互,使用其他技术实现的系统的功能;同时其他技术和系统也可以通过 JNI 提供的相应原生接口开调用 Java 应用系统内部实现的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1072048.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python对excel数据表进行数据清洗

当拿到excel表&#xff0c;使用python对excel操作前&#xff0c;第一件事情是对excel表的数据进行数据清洗。 数值是否有空值&#xff0c;是否有重复的数据&#xff0c;把以上2个问题解决完成以后&#xff0c;才是对数据真正操作的开始。 1、使用pandans读取数据 2、判断exce…

安装hadoop,并配置hue

0、说明 对于大数据学习的初始阶段&#xff0c;我也曾尝试搭建相应的集群环境。通过搭建环境了解组件的一些功能、配置、原理。 在实际学习过程中&#xff0c;我更多的还是使用docker来快速搭建环境。 这里记录一下我搭建hadoop的过程。 1、下载hadoop 下载地址&#xff1a;…

2023年中国智慧医院市场现状及行业发展趋势分析[图]

自&#xff12;&#xff10;&#xff11;&#xff19;年开始&#xff0c;国家卫健委首次提出了智慧医院这一概念ꎬ并明确提出包含的三个方面&#xff0c;即:一是智慧医疗方面&#xff0c;主要面向医院所有医务人员使用者&#xff0c;以深层次提升医院信息化建设为目标ꎬ规范参照…

2023年中国连锁麻辣烫市场发展趋势分析:连锁麻辣烫行业规模将持续增长[图]

连锁麻辣烫即以连锁模式经营的麻辣烫。麻辣烫源自巴蜀地区&#xff0c;后经过市场从业者的改良推广&#xff0c;已经成为一种表现形式丰富多样的中式快餐。可根据连锁麻辣烫门店连锁模式的不同&#xff0c;将连锁麻辣烫门店分为直营连锁麻辣烫和加盟连锁麻辣烫。 连锁麻辣烫行…

无痛入门Prometheus:一个强大的开源监控和告警系统,如何快速安装和使用?

一、前生今世 Prometheus 是完全开源的系统监控和告警工具包&#xff0c;它受 Google 内部的 BorgMon 监控系统启发&#xff0c;由前Google 工程师从 2012 年开始在 SoundCloud 以开源软件的形式进行研发&#xff0c;自此以后&#xff0c;许多公司和组织都采用了Prometheus 作为…

Docker学习笔记 - 极简极入门级

Docker学习笔记 - 极简极入门级 文章目录 Docker学习笔记 - 极简极入门级前言架构命令docker pulldocker pushdocker imagesdocker rmidocker rundocker stop/start/restartdocker rmdocker psdocker execdocker logsdocker save/loaddocker inspectdocker builddocker network…

2023年中国香樟木家具行业分析:定制化和个性化的需求不断增加[图]

香樟木是一种常见的木材&#xff0c;它在家具行业中被广泛使用。香樟木家具行业定义是指使用香樟木材料制造的家具产品的生产、销售和相关业务活动。 香樟木家具行业分类 资料来源&#xff1a;共研产业咨询&#xff08;共研网&#xff09; 香樟木家具的生产主要集中在少数大型…

vue-slot插槽

作用&#xff1a;让父组件可以向子组件中任意位置插入html结构&#xff0c;也是组件通信方式的一种&#xff0c;适用于父组件》子组件 分类: 默认插槽、具名插槽、作用域插槽 定义子组件时使用slot组件&#xff0c;在使用子组件是可以决定是否插入具体的html代码 默认插槽 如…

Survey on Cooperative Perception in an Automotive Context 论文阅读

论文链接 Survey on Cooperative Perception in an Automotive Context 0. Abstract 本文就协同基础设施领域提供相关环境的调查回顾了感知中涉及的主要模块&#xff1a;定位&#xff0c;目标检测和跟踪&#xff0c;地图生成提供了协同感知的 SWOT 1. Intro 无人驾驶汽车的背…

万宾荣获深圳应博会“全球应急产业先锋奖”创始人发表峰会演讲

今年5月&#xff0c;住房和城乡建设部表示将全面启动的城市基础设施生命线安全工程工作&#xff0c;通过各类智能感知设备等数字化手段&#xff0c;及早发现和管控城市燃气、桥梁、供水、排水防涝等领域的风险隐患&#xff0c;切实提高城市安全保障能力、维护人民生命财产安全&…

基于SpringBoot的足球联赛管理系统

基于SpringBoot的足球联赛管理系统 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 首页 详情 管理员界面 摘要 基于Spring Boot的足球联赛管理系统代表了一种现代体育赛事管…

这15个海运提单的雷区 你知道吗?

海运提单中英文对照 海运提单主要项目填制说明 1、托运人(Shipper)&#xff1a;即与承运人签订运输契约&#xff0c;委托运输的货主&#xff0c;即发货人。在信用证支付方式下&#xff0c;一般以受益人为托运人;托收方式以托收的委托人为托运人。另外&#xff0c;根据《UCP500》…

【uniapp】subnvue组件数据更新视图未更新问题

背景 : 页面中的弹窗使用了subnvue来写, 根据数据依次展示一个一个的弹窗, 点击"关闭"按钮关闭当前弹窗, 显示下一个弹窗 问题 : 当点击关闭时( 使用的splice() ), 数据更新了 , 而视图没有更新, 实际上splice() 是不仅更新数据, 也可以更新视图的 解决 : this.$fo…

Linux配置JAVA_HOME

[rootlocalhost ~]# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.3 LTS Release: 22.04 Codename: jammy1.下载jdk 官网下载地址 2.将下载好的软件包解压到指定目录 tar -zxf /usr/local/software/jdk-…

雪莱的式子武汉2023(分析+快速幂)

传送门&#xff1a;登录—专业IT笔试面试备考平台_牛客网 思路&#xff1a; 对于每一种质因子&#xff0c;如果他在μ&#xff08;&#xff09;函数中出现两次&#xff0c;那这种情况对答案贡献为0&#xff0c;所以我们可以只讨论每一种因子出现0&#xff0c;1次的情况。 对于…

Altium Designer实用系列(一)----原理图导入PCB、PCB板子外形、多层板绘制等

一、原理图导入PCB 绘制原理图就不必多说了&#xff0c;根据自己电路的需求&#xff0c;去设计电源、芯片的外围电路、MCU外设分配就好。接下来主要介绍的是在导入PCB前对原理图的检查&#xff1a; 元器件标号注解 元器件封装确认&#xff1a;工具->封装管理器&#xff1…

美团代运营优势成都优优聚告诉你!

美团代运营是一种全新的商务服务模式&#xff0c;通过委托美团平台进行运营管理&#xff0c;以提升企业销售业绩和品牌影响力为目标。美团代运营有着许多优势&#xff0c;下面我们来详细了解一下。 首先&#xff0c;美团代运营具有强大的用户流量。作为中国最大的本地生活服务平…

wpf中prism框架

安装prism包&#xff1a; 添加引用 using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Linq; using System.Threading.Tasks; using System.Windows; using Prism.DryIoc; using Prism.Ioc;namespace PrismDemo …

一文了解高压放大器的应用

高压放大器是一种能够将低压信号放大到高电压信号的设备或电路。它在许多领域中都有广泛的应用。以下是一些常见的高压放大器的应用领域。 实验室和科学研究&#xff1a;高压放大器在实验室研究和科学领域中扮演着重要的角色。例如&#xff0c;在物理学、化学、生物学等领域的研…

CRM系统软件如何防止客户流失

客户资源是很重要的资产&#xff0c;防止客户数据丢失是所有企业都非常重视的事情。在日常经营管理中&#xff0c;员工离职、工作交接、数据整理都会造成客户数据丢失的情况。作为企业管理工具&#xff0c;CRM客户管理系统都有哪些手段防止客户流失&#xff1f; 1、客户资源存…