深度剖析JVM三个面试常考知识点

news2024/11/18 6:01:58

目录

🐳今日良言:只要你足够努力,生命都会庇佑你

🐇一、JVM内存区域划分

🐇二、类加载过程

🐇三、垃圾回收机制(GC)


🐳今日良言:只要你足够努力,生命都会庇佑你

🐇一、JVM内存区域划分

先来了解一下什么是JVM :

JVM 是 Java Virtual Machine 的简称,意为 Java虚拟机。

虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统。

常见的虚拟机:JVM、VMwave、Virtual Box。

JVM 和其他两个虚拟机的区别:

1. VMwave与VirtualBox是通过软件模拟物理CPU的指令集,物理系统中会有很多的寄存器;

2. JVM则是通过软件模拟Java字节码的指令集,JVM中只是主要保留了PC寄存器,其他的寄存器都进

行了裁剪。

JVM 是一台被定制过的现实当中不存在的计算机。

对jvm有了简单的认识以后,接下来重点介绍一下 JVM 相关的三个面试常考知识点.

JVM内存区域划分:

JVM 是一个应用程序,一个 JVM 就是一个 java 进程,JVM 在启动的时候,会从操作系统这里申请到一整个很大的内存区域.

JVM 就根据需要将整个空间分成5个部分,各个部分都有不同的作用和功能.如下图:

 本地方法栈:

native 表示是 JVM 内部的 C++ 代码.

本地方法栈就是给调用native方法(JVM内部方法)准备的栈空间,存储的是native方法之间的调用关系.

程序计数器:

记录当前线程执行到哪个指令,每个线程有一份.

虚拟机栈:

给 java 代码使用的栈空间.存储的是方法之间的调用关系.

整个栈内部空间,可以认为是包含很多个元素(每个元素表示一个方法),每个元素又叫做"栈帧",每个栈帧里面包含了这个方法的入口地址、局部变量、参数、返回地址等.每个线程有一份.

堆:

整个 JVM 空间最大的区域, new 出来的对象就是在堆上,类的成员变量也是在堆上.

元数据区(又叫方法区):

存储的是类对象、静态成员、常量池等.

这里相关的面试题最主要的考点是,给一段代码,问某个变量在哪个区域上:

1.局部变量在虚拟机栈上.

2.普通成员变量在堆上.

3.静态成员变量在元数据区/方法区.


🐇二、类加载过程

介绍完了 JVM 的内存区域划分,再介绍第二个知识点:类加载过程.

类加载过程就是将一个 .class 文件(字节码文件, .java文件通过javac(java编译器)得到),从文件(硬盘)加载到内存(元数据区)的过程.

主要有以下流程:

 加载

通过双亲委派模型找到 .clas 文件,打开文件,将文件内容读到内存中.

验证

检查 .class 文件的格式对不对. .class 文件是一个二进制文件.

准备

给类对象分配内存空间(在元数据区占个位置),将静态成员设置成0值.

解析

初始化字符串,将符号引用转为直接引用.

初始化

调用构造方法,进行成员初始化,执行静态代码块,代码块,加载父类等...

发生类加载的时机:

1)构造类的实例.

2)调用类的静态方法/使用类的静态属性.

3)加载子类之前先加载其父类. 

 双亲委派模型:

双亲委派模型描述的就是上述加载阶段找 .class 文件的过程.

JVM 默认提供了三个类加载器.

1) BootstrapClassLoader

    负责加载java标准库中的类.(java规范,无论是哪种 JVM 的实现,都会提供的一样的类)

2)ExtendsionClassLoader

    负责加载 JVM 扩展库中的类.(java规范之外,由实现 JVM 的厂商提供的额外的类)

3)ApplicationClassLoader

    负责加载用户项目/用户提供的第三方库 中的类.

上述这三个类存在"父子关系",并不是继承中的父类和子类,而是每个类加载器中有一个parent 属性,指向自己的父亲---类加载器.

上述类加载器配合流程如下:

首先,加载一个类从ApplicationClassLoader开始,但是 ApplicationClassLoader 并不是真的加载,而是交给自己的父类加载,于是 ExtendsionClassLoader 开始加载, 但是也不是真的加载,而是交给自己的父类加载器BootstrapClassLoade,BootstrapClassLoader发现自己的parent属性为null,于是自己开始加载,搜索自己负责的标准库目录相关的类,找到了就加载,找不到就交给自己的子类加载器,于是ExtendsionClassLoader开始加载,搜索自己负责的扩展库目录相关的类,找到了就加载,找不到就交给自己的子类加载器,于是ApplicationClassLoader开始加载,搜索用户项目相关的类,找到了就加载.找不到就交给自己的子类加载器,但是此时ApplicationClassLoader的子类加载器为空,于是报类找不到这样的异常.

大致流程图如下:

为什么要有上述顺序呢?

上述这套顺序其实是出自于 JVM 实现代码的逻辑.

这段代码大概是类似于"递归"的方式写的.

这个顺序最主要的目的就是为了让Bootstrap能够先加载,Application最后加载,这就可以避免因为用户创建了一些奇怪的类,引起不必要的bug.

比如说:一个用户在自己的代码中写了一个 java.lang.String 这个类,按照上面的类加载流程,此时 JVM 加载的还是标准库中的类,不会加载到用户自己写的这个类,这样就能保证,即使出现上述情况,也不会让 JVM 内部的代码混乱,最多就是让用户自己写的这个类不生效. 


🐇三、垃圾回收机制(GC)

接下来介绍一下最后一个知识点:垃圾回收机制.

首先理解一下什么是"垃圾"?

Java中的垃圾指的是:不再使用的内存.

垃圾回收就是将不再使用的内存自动释放.

GC是最主流的解决垃圾回收的一种方式.

GC优点:非常省心,让程序员写代码简单点,不容易出错.

GC缺点:需要消耗额外的系统资源,也有额外的性能开销.

另外,GC这里还有一个比较关键的问题:STW(Stop The World):

如果有时候,内存中的垃圾太多了,此时触发一次GC操作,开销可能非常大,大到可能就把系统资源吃了很多,另一方面,GC回收垃圾的时候可能会涉及到一些 锁操作,导致业务代码无法正常执行,这样的卡顿,极端情况下,可能出现几十毫秒甚至上百毫秒.

GC主要是针对 堆 进行释放的.

GC是以"对象"为基本单位进行回收的!!(不是字节).

GC回收的是整个对象都不再使用的情况.

这样的设定就是为了"简单".

GC实际工作过程:

1.找到垃圾/判定垃圾

2.进行垃圾释放

1.找到垃圾/判定垃圾

  java中通过引用来使用一个对象,如果没有引用指向该对象了,说明这个对象就不再使用了.

  具体如何知道对象是否有引用指向呢? 

  两种典型实现:

  1).引用计数

       给每个对象分配一个计数器,只要有引用指向这个对象就让计数器+1,指向这个对象的引用销毁了,就让这个计数器-1.

         

{
   Test t = new Test();   // Test 对象的引用计数 1
   Test t2 = t;           // Test 对象的引用计数 2
   Test t3 = t;           // Test 对象的引用计数 3
}

        这是一种简单有效的办法,但是会带来一定问题:

         内存空间浪费的多(利用率低):每个对象都要分配一个计数器,如果按4个字节算,代码中

         的对象非常少,无所谓,如果对象特别多,占用的额外空间就会很多,尤其是每个对象比较

         小的情况.  

         如果一个对象1k,此时多4个字节无所谓.

         如果一个对象4个字节,此时多4个字节,体积扩大一倍.

         还存在循环引用的问题:

         

class Test {
   Test t = null;
}

   Test a = new Test();  // 1号对象,引用计数是1
   Test b = new Test();  // 2号对象,引用计数是1
   a.t = b;              // a.t也指向2号对象,2号对象引用计数是 2 了.
   b.t = a;              // b.t也指向1号对象,1号对象引用计数是 2 了.

    接下来,如果a和b引用销毁了,1号对象和2号对象的引用计数都-1,但是结果都是1,不是0,

    虽然不是0,不能释放资源,但是这两个对象都无法访问到了.

    2).可达性分析

    java中的对象都是通过引用来访问的,通常是一个引用指向一个对象,这个对象里的成员又 

    指向其它对象.(比如二叉树的节点)

    可达性分析就是把组织这些所有对象的结构视为是树,就从根节点开始遍历树,所有能访

    问到的节点标记为"可达"(不能访问的就是不可达).

    JVM 自己捏着一个所有对象的名单.通过上述遍历,把可达的标记出来,不可达的就是垃圾

    进行释放.

    可达性分析需要进行类似于"树遍历",这个操作相比于引用计数来说是要慢一些的,但是速度

    慢是没关系的,上述可达性分析遍历操作,并不需要一直执行,只需要每隔一段时间,分析一遍

    就可以了.

    进行可达性分析遍历的起点,称为GCroots,可以作为GCroots的有:

    栈上的局部变量,常量池中的对象,静态成员变量.

    一个代码中,有很多个这样的起点,把每个对象都往下遍历一遍就完成了一次扫描过程.

2.进行垃圾释放

   主要是三种基本做法.

   1)标记清除

      简单粗暴,直接标记垃圾,然后进行释放,但是会带来"内存碎片问题",被释放的空间是零散

      的,不是连续的.

      

     申请空间要求的是连续空间,上述总的空闲可能很大,但是具体的每一个空间又很小,可能

     导致申请大一点的内存就会失败.比如总的空闲内存是10k,分成1k一个,此时申请2k的空

     间就会失败.

   2)复制算法

      解决了"内存碎片问题".将一块大的内存空间分成两部分,用一半丢一半.

      复制算法就是把不是垃圾的对象复制到另一半,然后将整个空间删除.每次触发复制算法,

      都是向另一半进行拷贝.

      

 

        复制算法的缺点:1.空间利用率低(用一半丢一半) 2.(垃圾少的话,复制成本比较大)

3)标记整理

     解决了复制算法的缺点.

     类似于顺序表删除中间元素,会有元素搬运的操作.

      保证了空间利用率,同时也解决了"内存碎片问题".但是这种做法,效率也不高,如果要搬运

      的空间比较大,此时开销也很大.

   

基于上述基本策略,搞了一个复合策略"分代回收"

分代是怎么分的呢?

  分代是基于一个经验规律:如果一个东西,存在的时间比较长了,那么大概率还会持续长时间的存在下去.(要没早就没了)

  上述规律,对于 java 中的对象也是有效的, java 的对象要么生命周期特别长,要么生命周期特别短,根据生命周期的长短,分别使用不同的算法.

  给对象引用一个概念:年龄 (不是以年为单位,而是熬过GC的轮次),年龄越大,这个对象存在的时间就越久.

  熬过GC的轮次是指:经过一轮可达性分析遍历以后,发现这个对象不是垃圾.

  堆划分成两个区域:新生代和老年代

  新生代又划分成三个区域:一个比较大的伊甸区,两个比较小且一样大的幸存区.

  刚 new 出来的对象,年龄是0,放在伊甸区.

  熬过一轮GC以后,就要放到幸存区了.从伊甸区-->幸存区使用复制算法.

  对象到了幸存区以后,就要周期性的接受GC的考验.

  如果变成垃圾就要释放,如果不是垃圾,就要拷贝到另外一个幸存区(两个幸存区同一时刻只能使用一个).在二者之间使用复制算法来回拷贝.由于幸存区体积不大,此处的空间浪费也能接受,

  如果这个对象在两个幸存区来回拷贝很多次了,此时就要进入老年代了.

  老年代都是年纪大的对象,生命周期周期普遍长.

  这个对象在老年代也要周期性的接受GC的扫描,但是扫描频率更低了.

  如果老年代的对象是垃圾了,就要使用标记整理的方式进行释放.


 

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

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

相关文章

MySql的sql_mode

文章目录简介查看命令配置文件设置命令配置文件常用的模式ONLY_FULL_GROUP_BYNO_AUTO_VALUE_ON_ZEROSTRICT_TRANS_TABLESNO_ZERO_IN_DATENO_ZERO_DATEERROR_FOR_DIVISION_BY_ZERONO_AUTO_CREATE_USERNO_ENGINE_SUBSTITUTIONPIPES_AS_CONCATANSI_QUOTES专栏目录请点击 简介 他是…

【通过Cpython3.9源码看看列表到底是咋回事】

列表结构 typedef struct {PyObject_VAR_HEAD/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */PyObject **ob_item;/* ob_item contains space for allocated elements. The number* currently in use is ob_size.* Invariants:* 0 < ob_siz…

vulnhub DC:3.2渗透笔记

kali ip :192.168.20.130 靶机下载地址:https://www.vulnhub.com/entry/dc-32,312/ 信息收集 扫描靶机ip以及开放端口 开放了80端口访问一下 Welcome to DC-3. This time, there is only one flag, one entry point and no clues. To get the flag, youll obviously have …

【Java版oj】day29求正数数组的最小不可组成和、有假币

目录 一、求正数数组的最小不可组成和 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 二、有假币 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码…

三、用户与权限管理

五、角色管理 1、角色的理解 引入角色的目的时方便管理相同权限的用户。只需要给相同权限的用户分配角色即可&#xff0c;而不需要分配具体的权限 2、创建角色 创建角色使用 CREATE ROLE 语句&#xff0c;语法如下&#xff1a; CREATE ROLE role_name[host_name] [,role_…

干货分享 | 常用车载总线CAN、CANFD、LIN、FlexRay 和 Ethernet概述

随着现代汽车的电子化程度越来越高&#xff0c;汽车总线系统也变得越来越复杂。汽车总线测试是一项重要的任务&#xff0c;它有助于确定车辆电子系统中的问题&#xff0c;并保障车辆的安全和可靠性。本文将介绍五种常见的汽车总线系统和相关的测试工具。 CAN总线 …… 控制器…

传输线的物理基础(八):用近似值和二维场计算特性阻抗

设计一个特定的目标特性阻抗实际上是调整线宽、电介质厚度和介电常数的问题。如果我们知道传输线的长度和导体周围材料的介电常数&#xff0c;并且我们可以计算出特征阻抗&#xff0c;我们就可以使用上面的关系来计算所有其他参数。 当然&#xff0c;每一种不同类型的横截面几…

kaggle竞赛 - Stable Diffusion - Image to Prompts

演绎提示&#xff0c;生成我们的“高度详细&#xff0c;锐利的焦点&#xff0c;插图&#xff0c;宏伟&#xff0c;史诗”图像的3d渲染 1.比赛目标 这个竞赛的目标不是从文本提示生成图像&#xff0c;而是创建一个模型&#xff0c;可以在给定生成图像的情况下预测文本提示&…

曲线平滑算法:三次Hermite曲线生成

目录 1.三次Hermite曲线的参数方程 2. 三次Hermite曲线的绘制 Hermite曲线是通过给定曲线的两个端点的位置矢量、以及两个端点处的切线矢量、来描述曲线的&#xff0c;如图1所示。这里先对Hermite曲线进行数学公式推导&#xff0c;然后讲述如何绘制Hermite曲线。&#xff08;这…

【Ansys】什么软件模块是DS,它和workbench、mechanical的区别在哪里?

一、DesignSpace和workbench 早期的Workbench称之为DesignSpace&#xff0c;更偏向于建模。 现在DS是license的一种&#xff0c;而分析的模块在11中称之为Simulation&#xff08;Design Simulation&#xff09;&#xff0c;在12中改名为Mechanical。 所以&#xff0c;你可以…

SpringBoot整合RabbitMQ(六种工作模式介绍)

介绍 RabbitMQ是一种消息中间件&#xff0c;用于处理来自客户端的异步消息。服务端将要发送的消息放入到队列池中。接收端可以根据RabbitMQ配置的转发机制接收服务端发来的消息。 RabbitMQ依据指定的转发规则进行消息的转发、缓冲和持久化操作&#xff0c;主要用在多服务器间或…

Unity开发数字化看板-通用的设备运动同步

通用的设备运动同步 通过获取实时采集运动位置&#xff0c;发送到unity程序中&#xff0c;通过比例运算&#xff0c;转换成模型的运动位置&#xff0c;实现虚实同步。 在工业设备中&#xff0c;复杂的运动进行分析、分解&#xff0c;最本质的的运动就是平移和转动&#xff0c…

美团到店暑期实习Java一面

目录1.rabbitmq如何避免消息丢失 &#xff08;三个阶段&#xff09;2.如何保证消息的顺序性3.如何保证消息不被重复消费4.缓存与数据库的不一致性5.redis的过期策略和内存淘汰策略6.简单说下LRU的实现7.用原生redis实现分布式锁&#xff0c;锁误删的情况8.锁续期如何去考量9.缓…

MySQL-----库的操作

文章目录前言一、创建数据库二、创建数据库实例三、字符集和校验规则1.查看系统默认字符集以及校验规则.2.查看数据库支持的字符集3.查看数据库支持的字符集校验规则4.校验规则对数据库的影响四、操纵数据库1.查看数据库2.显示创建语句4.修改数据库5.删除数据库6.备份和恢复6.1…

Diffusion模型系列文章

DDPM 论文 扩散模型包括两个过程&#xff1a;前向过程&#xff08;forward process&#xff09;和反向过程&#xff08;reverse process&#xff09;&#xff0c;其中前向过程又称为扩散过程&#xff08;diffusion process&#xff09;&#xff0c;如下图所示&#xff0c;从x…

【音视频第8天】mediasoup拥塞控制【未完待续】

WebRTC的拥塞控制方式主要有以下几个&#xff1a;Transport-cc、BBR-congestion、remb&#xff08;BBR已被google从webrtc移除了&#xff09;。mediasoup支持Transport-cc和remb。 一、前言 实时通信的延时指标 视频服务质量指标 音视频服务质量与带宽之间的矛盾、实时性与服…

【微信小程序】初识微信小程序组件

作者简介&#xff1a;一名C站萌新&#xff0c;前来进行小程序的前进之路博主主页&#xff1a;大熊李子&#x1f43b; 一、组件的创建与引用 1.1 创建组件 在项目的根目录中&#xff0c;鼠标右键&#xff0c;创建 components -> test 文件夹在新建的 components -> test…

NLP / LLMs中的Temperature 是什么?

ChatGPT, GPT-3, GPT-3.5, GPT-4, LLaMA, Bard等大型语言模型的一个重要的超参数 大型语言模型能够根据给定的上下文或提示生成新文本&#xff0c;由于神经网络等深度学习技术的进步&#xff0c;这些模型越来越受欢迎。可用于控制生成语言模型行为的关键参数之一是Temperature …

[译]自下而上认识Elasticsearch

注意:原文发表时间是13年,所以实现有可能与新版不一致. 原文地址:https://www.elastic.co/cn/blog/found-elasticsearch-from-the-bottom-up Introduction 在本系列文章中,我们从一个新的视角来看ElasticSearch.我们将从下往上,从抽象的底层实现到用户可见层,我们在向上移动的…

【JaveEE】网络编程之TCP套接字、UDP套接字

目录 1.网络编程的基本概念 1.1为什么需要网络编程 1.2服务端与用户端 1.3网络编程五元组 1.4套接字的概念 2.UDP套接字编程 2.1UDP套接字的特点 2.2UDP套接字API 2.2.1DatagramSocket类 2.2.2DatagramPacket类 2.2.3基于UDP的回显程序 2.2.4基于UDP的单词查询 …