【JVM篇1】认识JVM,内存区域划分,类加载机制

news2024/9/28 3:26:51

目录

一、JVM内存区域划分

①程序计数器(每个线程都有一个)

②栈:保存了局部变量和方法调用的信息(每一个线程都有一个栈)

       如果不停地调用方法却没有返回值,会产生什么结果

③堆(每一个进程都有一个堆,线程共享一个堆)

       如何区分一个变量是处于栈上还是堆上呢?

④方法区(存放的是类对象)

类对象是什么

二、类加载机制

步骤1:Loading

步骤2:Linking(验证、准备、解析)

①验证:验证Class文件是否符合规范

②准备:给静态变量分配内存

 ③解析:初始化类的常量池当中的一些常量

步骤3:初始化(初始化对象,为静态属性赋值) 

双亲委派机制

双亲委派模型

JVM类加载器是什么(3个类加载器)

标准库的String类是怎样被加载的

自定义的Test类是怎加加载的

双亲委派模型的好处

 类加载一定要双亲委派模型吗


一、JVM内存区域划分

JVM的内存区域被划分为了以下4个部分:

程序计数器、栈、堆、方法区。

下面,将分别介绍这几个区是干什么的。


①程序计数器(每个线程都有一个)

       这一个区域是内存当中最小的区域。保存了下一条要执行的指令的地址在哪里。

       指令:就是字节码。程序要运行,JVM就得把字节码加载起来,存放到内存当中。

       当程序把一条条指令从内存当中取出来,放到CPU上面执行的时候,也需要随时记住执行到哪一条了(因为CPU是并发执行命令的,不是只给一个进程提供服务的)

       每一个线程都会有一个程序计数器。(因为操作系统是以线程为单位进行调度的)每一个线程都需要记录自己执行的位置。

     


②栈:保存了局部变量和方法调用的信息(每一个线程都有一个栈)

       当每调用一个新的方法的时候,都会涉及到"入栈"操作。每执行完一个方法的时候,就会把这一个方法从内存栈当中移除。

       当A方法内部调用B方法,然后在B方法内调用C方法的结果是怎样的呢?

       首先,会在内存栈当中存放A方法的有关信息。然后,调用B方法的时候,在栈中存放B方法的信息。最后调用C方法的时候,会在栈当中存放C方法有关的信息。

       此处,"有关的信息"有:方法的局部变量、方法传入的参数(形参)、调用的位置、返回的位置等等信息。

       当方法执行完毕(或者return)之后,方法对应的信息也会随之从内存栈上面消失。每一个存放方法的区域被称为一个"栈帧"。 

       如果不停地调用方法却没有返回值,会产生什么结果

        

         运行一下程序,可以发现:

         此处抛出了一个错误信息:StackOverflowError

        JVM的栈空间是比较小的,但是也就一般几M或者几十M,因此在上述的调用过程当中栈很有可能会满了的。 

          

对于栈来说,每一个线程都有一个栈。不同的线程有不同的栈


③堆(每一个进程都有一个堆,线程共享一个堆)

       堆是内存内存空间当中最大的区域。new出来的对象,就是在堆当中的。那么也就意味着,对象的成员变量也是存储在堆当中的。

       如何区分一个变量是处于栈上还是堆上呢?

        局部变量(也就是方法内部创建的基本数据类型变量)都存储在调用这个方法的线程的栈上。

        成员变量new出来的对象,都存放在堆上面。但是方法内部对于对象的引用是保存在栈上面的。


④方法区(存放的是类对象)

方法区当中,存放的是一个类的.class对象(二进制字节码)。

这里的这个class对象,就是保存在方法区当中的。

类对象是什么

      类对象描述的就是它对应类当中的属性、方法、以及各自的权限描述符。

      此外,一个类当中的static方法、static属性也是属于类对象的。这些方法、属性又被称为"类方法"、"类属性"


二、类加载机制

简单来说,就是把.class文件,加载到内存当中,构建类对象

类加载分为3个步骤:

步骤1:Loading

步骤2:Linking

步骤3:Initialization

下面,详细说明一下每一个步骤是干什么的: 


步骤1:Loading

       先找到对应的.class文件,然后打开并且读取.class文件。同时初步生成一个类对象(但是不是真正使用的对象)。

        1)通过一个类的全限定名来获取此类的二进制字节流

        2)将这个字节流所代表的静态存储结构转化为方法运行时候的数据结构

        3)在内存当中生成一个代表此类的java.lang.Class对象。然后把这个Class对象,放入到方法区当中,作为方法区这个类的各种数据的访问入口。

下面,来看一下这个二进制的.class文件究竟包含了什么。

下图就是一个ClassFile的图示。

其中,左边的u4代表的就是,u2等信息代表的是占了多少个字节。

u4就是4个字节的unsigned int。u2就是2个字节的unsigned int。


步骤2:Linking(验证、准备、解析)

由上图,也可以看到在连接部分分为了三个步骤:验证准备解析

首先,看一下"验证"这个部分是做什么的


①验证:验证Class文件是否符合规范

         确保Class文件字节流中包含的信息符合《Java虚拟机规范》的全部约束要求。如果想读取Class文件的内容,就需要先验证一下是否符合规范。验证的内容有:

    文件格式验证;

    字节码验证;

    符号引用验证。


②准备:给静态变量分配内存

例如,给static修饰的变量分配内存,并且设置上初始的值:也就是默认值。

例如下面的代码当中,有一个属性为id,它被static修饰,并且它的值为123。

但是在现在这个阶段,它真实的值还是0。

class Member{

    private static int id=123;
    private String name;
}

 ③解析:初始化类的常量池当中的一些常量

       在前面我们也提到了,.class文件当中包含了一些常量,每一个常量都有一个编号。那么这个时候,就是初始化一些常量的时候了。


步骤3:初始化(初始化对象,为静态属性赋值) 

       此时,就是针对对象进行初始化的操作,在这一步的基础上产生对象。

       同时,在这一个步骤上,会把静态的变量给赋值上它对应的值。

       例如在前面的时候,提到了在准备阶段,为一个静态的变量赋上默认的值。但是并没有为它赋值上真正的值。那么就是在初始化的阶段为它赋值上真正的值。


双亲委派机制

首先,先来体验一下双亲委派机制。

让一个类B继承自A。然后在A这个类当中包含以下几个内容:

A的构造方法、一个构造代码块、一个静态代码块。

 然后,让B继承自A,在B这个类的内部,包含以下的几个内容:

B的构造方法、B的构造代码块、B的静态代码块。

       最后,令一个Test类继承自B类,并且在Test类当中包含一个mian方法。main方法当中连续两次调用new Test()。

class A {

    public A() {
        System.out.println("A的构造方法");
    }

    {
        System.out.println("A的构造代码块");
    }

    static {
        System.out.println("A的静态代码块");
    }
}

class B extends A {
    public B() {
        System.out.println("B的构造方法");
    }

    {
        System.out.println("B的构造代码块");
    }

    static {
        System.out.println("B的静态代码块");
    }

}


/**
 * @author 25043
 */
public class Test1 extends B {
    public static void main(String[] args) {
        System.out.println("第一次new");
        new Test1();
        System.out.println("第二次new");
        new Test1();
    }
}

  运行之后,结果是:

根据以上的特点,可以得出来,双亲委派机制加载类实例的几个原则:

1、类加载首先需要加载静态的代码块:先父类静态代码块,然后子类静态代码块

2、静态代码块只会在类加载的时候执行1次。若重复加载(重复new对象)那么只会执行一次。

3、构造代码块和构造方法每一次new都会执行。并且构造代码块一定优先于构造方法执行。

4、无论是静态还是实例代码块,一定都是父类子类之前。 

总结一下,那就是:

静态优先且唯一、父类优先、代码块优先。 


为什么在输出"第一次new"之前,先输出了"第一次加载A"和"第一次加载B"呢?

       因为:如果想要执行main方法,首先需要加载Test类。但是由于Test继承于B类,然后B类又继承于A类。因此,会首先加载顶级父类A的静态代码块,然后再加载下一级父类的静态代码块。


双亲委派模型

在上面的文章当中,我们提到了,类的加载分为3个阶段:

 第一阶段:Loading阶段;

 第二阶段:Linking阶段;

 第三阶段:Initialing阶段。

        Loading阶段,主要负责的就是加载一个类的字节码文件,并生成一个Class对象

       而双亲委派模型,描述的是JVM当中的类加载器,如何根据全限定名:类名+包名(例如Java.lang.String)找到.class文件的过程,这个过程属于Loading阶段当中比较靠前的阶段。

       


JVM类加载器是什么(3个类加载器)

JVM的类加载器主要是以下的3个:

1、BootStarpClassLoader:负责加载标准库当中的类(例如String、List等等)

2、ExtensionClassLoader:负责加载JDK当中的扩展类

3、ApplicationClassLoader:负责加载当前目录当中的类。

每一个类加载器负责加载自己对应的目录。 

 而上述的双亲委派模型,就描述了找目录的过程,上述3个类加载器是怎样进行配合的。


下面,举一个例子:

标准库的String类是怎样被加载

       第一步:程序启动,先进入ApplicationClassLoader类加载器。

       第二步:然后在ApplicationClassLoader当中检查一下,它的父加载器(ExtensionClassLoader)是否已经加载过了。如果没有加载过,那么就调用ExtensionClassLoader来进行加载。

       第三步:ExtensionClassLoader也会检查一下,它的父加载器(BootStarpClassLoader),是否加载过。如果没有,那么就调用最高的父加载器(BootStarpClassLoader)来进行加载。

       然后查找标准库的目录:Java.lang.String,并且完成Java.lang.String的加载。


自定义的Test类是怎加加载的

       自定义的Test类,也会经过上述

       由ApplicationClassLoader==>ExtensionClassLoader==>BootStrapClassLoader的三个加载过程。

       但是,由于BootStrapClassLoader负责的目录是标准库的目录,那么肯定找不到Test类,于是回到下一级的目录:ExtensionClassLoader进行加载。同样,也找不到Test类。最后,回到ApplicationClassLoader负责的目录,也就是当前项目的目录进行加载,最终找到了Test类,进行加载。

 如果在最后的阶段,也没有找到Test类,那么就会抛出一个异常:ClassNotFoundException


双亲委派模型的好处

       当用户自定义的类如果和派生类/标准库当中的类如果全限定名(类名称+包名称)重复了,仍然可以准确地加载标准库当中的类,而不是加载用户自定义的类    

       在上述过程当中,如果查找到标准库当中有Java.lang.String这个类,就不会再回去加载了

       此处,我自定义一个类(java.lang.String)

 但是,如果在其他的地方进行new,发现new的是标准库当中的类。  


 类加载一定要双亲委派模型吗

       不一定,双亲委派模型只是JVM内部实现的一个类加载机制。

       例如Tomcat加载webapps当中的类就没有使用双亲委派模型,因为Tomcat当中的类都是已经被开发好了的,无需多一道从标准库回去查询的工序。

       双亲委派模型只是避免用户自定义的类标准库当中的类全限定名重名时候,加载了用户的类。但是如果已经确定了用户自定义的类的全限定名和标准库的不一致,那么就没有必要多去检验一次父加载器当中是否有加载了。


 

 

 

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

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

相关文章

【C++】C++11新特性——基础特性

文章目录一、列表初始化1.1 {}初始化1.2 initializer_list类型二、类型推导2.1 auto2.2 auto注意事项2.3 decltype三、新增与改进3.1 nullptr3.2 范围for3.3 array3.4 forward_list3.5 unordered系列3.6 final与override一、列表初始化 1.1 {}初始化 C11 引入了一个新的初始化…

[数据结构与算法(严蔚敏 C语言第二版)]第1章 绪论(章节题库+答案解析)

练习 选择题 算法的计算量的大小称为计算的( )。 A.效率 B.复杂性 C.现实性 D.难度 计算机算法指的是解决问题的步骤序列,它必须具备( )三个特性。 A.可执行…

“快速掌握如何用FFmpeg在Python中截取定时间隔的MP4视频画面“

目录 简介: 源代码: 源代码说明: 这段代码中,首先定义了输入视频文件名、字体文件路径和输出图像文件名格式。然后使用subprocess模块的call函数调用FFmpeg命令。FFmpeg命令被定义为一个列表,其中每个元素都是命令中…

RocketMQ5.0.0消息消费<二> _ 消息队列负载均衡机制

目录 一、消费队列负载均衡概览 二、消费队列负载均衡实现 1. 负载均衡UML 2. 启动RebalanceService线程 3. PUSH模式负载均衡 三、负载均衡策略 四、参考资料 一、消费队列负载均衡概览 RocketMQ默认一个主题下有4个消费队列,集群模式下同一消费组内要求每个…

合并链表相关的练习

目录 一、合并两个有序链表 二、两数相加 一、合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4] 示例 2&…

熬夜30天吃透这九大Java核心专题,我收割了3个大厂offer

这次一共收割了3个大厂offer,分别是蚂蚁金服、美团和网易,特意分享这次对我帮助非常大的宝典资料,一共涉及九大核心专题,分别是计算机网络、操作系统、MySQL、Linux、JAVA、JVM、Redis、消息队列与分布式、网站优化相关&#xff0…

MySQL8启动错误“Neither found #innodb_redo subdirectory, nor ib_logfile* files”

今天做MySQL备份文件回复测试,用来检验MySQL备份文件可用性。 MySQL版本8.0.32 备份文件为腾讯云MySQL实例,版本8.0 使用xtrabackup恢复备份。执行过程顺利,启动MySQL时发生错误。提示如下: 注意,这里使用了systemctl stop mysql。虽然启动失败了,但是如果不执行这条…

全国青少年软件编程(Scratch)等级考试一级真题——2019.9

青少年软件编程(Scratch)等级考试试卷(一级)分数:100 题数:37一、单选题(共25题,每题2分,共50分)1.小明在做一个采访的小动画,想让主持人角色说“大家好!”3秒…

AcWing 3555. 二叉树

第一种做法是dfs。但是注意,如果是非常单纯的dfs,要把每个节点可以到达的所有节点都记录下来(父节点两个子节点)。如果只记录了子节点,没记录父节点,就失去了一个方向,肯定出错。 例如求2和6之…

Git常见问题:Your branch and ‘xxx/xxx‘ have diverged

报这个错就是因为你和远程库出现分叉了,具体表现是是由于同一个分支有多人修改,多人commit造成的 用户1操作 用户2操作 T1 git co -b brv_1 git co -b brv_1 T2 git add test2.java git commit -m m2 git push T3 git add test1.java git comm…

VR全景博物馆,打造7*24小时的线上参访体验

导语:博物馆作为人们了解历史、文化和艺术的重要场所,现在可以通过VR全景技术来进行展览,让参观者身临其境地感受历史文化的魅力。本文将介绍博物馆VR全景的特点、优势,以及如何使用VR全景技术来丰富博物馆的展览和教育活动。什么…

数据库:Mysql数据库安装及使用

目录 一、数据库介绍 1、基本概念 2、数据库类型 3、版本演变 二、Mysql安装 1、官网下载yum安装 2、手动配置yum安装 三、Mysql基本操作 1、登录与改密 2、检测数据库健康 3、 库的创建与使用 4、数据类型 5、修饰符 6、表的创建与使用 7、分组查询 8、查询排…

hive之正则函数研究学习regex/regex_replace/regex_extract

首先学习这个之前要先知道一些正则的基本知识。 随便百度一下正则表达式 – 元字符 | 菜鸟教程 字符描述\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,n 匹配字符 "n"。\n 匹配一个换行符。序列 \\ 匹…

残酷现实:大部分的App小程序,日活<100

残酷现实:99%的APP小程序&#xff0c;日活<100 日活跃用户数量(DAU&#xff09;是一个核心指标 Daily Active Users 互联网的难度系数一路拉高 只有流过血的战士&#xff0c;才能意识到战场的残酷 趣讲大白话&#xff1a;赵本山小品台词&#xff0c; 残酷的现实已直逼我心理…

千访 | 互动千万!小红书博主修炼手册

采访手记&#xff1a; 截止到目前&#xff0c;姜老师的护肤成分室在小红书的粉丝量已达150W。在赛道内卷的美妆区&#xff0c;突破真人出镜讲解&#xff0c;采用二次元漫画&#xff0b;变美内容结合&#xff0c;成为护肤动画领域的头部大号。 接下来&#xff0c;就让我们通过…

[算法]希尔排序——插入排序的升级

参考&#xff1a;《漫画算法-小灰的算法之旅》 目录 1、希尔排序的思想 2、例子 3、希尔排序代码 4、希尔排序的优化 5、希尔排序是不稳定排序 问&#xff1a;什么情况下插入排序的工作量会比较小呢&#xff1f; 答&#xff1a;首先&#xff0c;当数组元素较小时&#xf…

基于Halcon的MLP(多层感知神经网络)分类器分类操作实例

一、介绍 人工神经网络(Artificial Neural Network,ANN)简称神经网络(Neural Network,NN)或类神经网络,是一种模仿生物神经网络的结构和功能的数学模型或计算模型,用于对函数进行估计或近似。 MLP神经网络是一种基于神经网络、动态的分类器。MLP分类器使用神经…

C51---智能感应垃圾桶

1.器件&#xff1a;C51、蜂鸣器、SG90舵机、HC-SR04超声波测距 2.实现功能&#xff1a;超声波感应&#xff0c;舵机转动打开垃圾桶&#xff0c;蜂鸣器发出提示&#xff0c;LED灯亮 3.代码&#xff1a; #include "reg52.h" #include "intrins.h" //距离小…

关于进程与进程调度

目录什么是进程进程管理进程的结构体(PCB)里的属性并行与并发什么是进程 一个运行起来的程序就是进程. 比如文件名是以 exe 结尾的就是一可执行文件(程序) 双击QQ.exe文件, 这个程序就跑起来了, 它在系统中形成了一个进程, 那我们怎么看到进程呢? 可以打开任务管理器, 点开进…

Spark MLlib 模型训练

Spark MLlib 模型训练决策树随机森林GBDTSpark MLlib 开发框架下 : 监督学习 : 回归 (Regression) , 分类 (Classification) , 协同过滤 (Collaborative Filtering)非监督学习 : 聚类 (Clustering) 、频繁项集 (Frequency Patterns) 例子分类 : 算法分类 : 算法分类算法子分类…