面试专题:JAVA虚拟机(1)

news2024/12/28 5:38:38

关于Java虚拟机,在面试的时候一般会问的大多就是①Java内存区域、②虚拟机垃圾算法、③虚拟机垃圾收集    器、④JVM内存管理、⑤JVM调优、⑥Java类加载机制这些问题了。推荐书籍《深入理解Java虚拟机:JVM高级特性    与最佳实践(第二版》、《实战Java虚拟机》。

用一张图展示关于jvm涉及的模块及他们的关联关系。

jvm处理的是被javac编译java后的class文件。即从class文件开始,被类加载器加载后在jvm内存中处理,jvm内存模型分两大类:一类是所有线程共享数据区(索引线程共享的数据区)包括:方法区和堆;另一类是每个线程独有的(线程隔离的数据区)包括:虚拟机栈、本地方法栈、程序计数器,共享的数据被执行引擎进行处理,线程独有的被本地库接口处理最终执行索引被本地库接口执行再最终被本地方法接口处理。还有GC收集器的相关内容。

那按照上图关于jvm虚拟机涉及到的知识:

1.Java代码的编译和执行:java文件的编译从java文件--》class文件;类加载及类加载器和著名的双亲委派机制;类执行机制(jvm执行引擎);

2.虚拟机参数

3.jvm内存模型(JMM)

4.对象的创建到对象的内存分配再到对象的访问

5.CG垃圾收集器,判断对象什么时候可以被回?对象收集方法有哪些?常见的垃圾收集器有哪些?
面试常见问题:
1.介绍下 Java 内存区域(运行时数据区)

java内存模型JMM(java manary model):JDK1.8之前,Java内存区域包括堆、方法区、虚拟机栈、本地方法栈、程序计数器,1.8之后使用元数据区替代了方法区。

Java内存区域是指 JVM运行时将数据分区域存储 ,简单的说就是不同的数据放在不同的地方。通常又叫 运行时数据区域。

Java内存模型(JMM)定义了程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。

 其中程序计数器、虚拟机栈、本地方法栈是线程独有的内存区,而堆和方法区是线程共享区;

1) 程序计数器(Program Counter Register):也叫PC寄存器,程序计数器是一块较小的内存,他可以看做是当前线程所执行的行号指示器。
如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;
字节码解释器工作的时候就是通过改变这个计数器的值来选取下一条需要执行的字节码的指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

看文字比较抽象直接上代码

public class PCRegister {
    public static void main(String[] args) {
        int x = 1;
        int y = 2;
        System.out.println(x+y);
    }
}

用Java 类解析器 javap对class文件进行反编译,查看分解的class文件。有两种方式

第一种:javac XXX.java  先将java类编译成class文件,再javap -v XXX.class就可以查看到了

第二种:插件jclasslib进行

第一种方式:

第一步:右键字节码文件打开控制台终端

第二步:在控制台输入命令:javap -v 字节码文件名称 

第三步:控制台打印的内容即是反编译后的,前面标数字的就是程序计数器的行号指示器

 如果没有class文件可以直接javac XXX.java 文件进行编译先生成class文件

 第二种方式idea安装插件

安装 jclasslib Bytecode Viewer 插件

选中PCRegister.java类 在工具栏view下拉找到 点击 Show Bytecode with Jclasslib

 然后是英文的,做一下本地语言设置就可以

 选中方法Methods -> 选中main -> 选中 Code, 即可查看字节码反编译后的内容

如上图,字节码文件反编译后可以看到有一系列 指令地址和 操作指令。比如前面的数字0,1,...这些就是指令地址,而他后面跟着的就是操作指令。程序计数器就相当于一个临时空间存储将要执行的指令地址当该指令地址对应的操作指令被执行引擎解释并执行后存储下一个指令地址。

要想让计算机执行程序,需要让执行引擎中的解释器将字节码操作指令解释成CPU能够识别的机器指令。

而选取哪一条操作指令进行解释并执行,这个时候就需要依赖于程序计数器了。可以把它想象成一个临时空间,用于存储字节码操作指令的指令地址。

本图中,0 就是一个指令地址,通过指令地址就能够找到哪条指令,说明当前需要选取执行的操作指令是:iconst_1。

如果执行完0后,需要执行指向1的这条指令,那么将程序计数器(PC计数器)中存储的指令地址改成1就行了。

注意:当虚拟机正在执行的方法是一个本地(native)方法的时候,jvm的pc寄存器存储的值是undefined。程序计数器是线程私有的,它的生命周期与线程相同,每个线程都有一个。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError 情况的区域。

2).虚拟机栈

内存栈 FILO(fist in last out)
描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于储存局部变量表、操作数栈、动态链接、方法出口等信息。

Java虚拟机栈(Java Virtual Machine Stack),早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的Java方法调用,是线程私有的。生命周期和线程的生命周期是一致的。作用:主管Java程序的运行,保存方法的局部变量,部分结果,并参与方法的调用和返回。

栈的特点:

栈是一种快速有效的分配储存方式,访问速度仅次于程序计数器。

JVM虚拟机栈的操作只有两个。

每个方法执行,伴随着进栈

方法执行结束后,伴随着出栈。

对于栈来说并不存在垃圾回收的问题,但是存在溢出的问题。

使用参数 -Xss选项来设置每个线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度。

每个线程都会创建一个独有的虚拟机栈,虚拟机栈由一个个栈帧组成,一个栈对应一个方法,栈帧包括四块内容:操作数栈、局部变量、动态连接、方法返回地址。

虚拟机栈有几个概念:栈顶和栈低,入栈和出栈。

如图是入栈从方法1--》调用方法2入栈---》调用方法3入栈--》调用方法4入栈,当方法4执行后从栈顶弹出然后方法3--》方法2--》方法1执行完毕弹出虚拟机栈,线程执行方法结束。

Java方法有两种返回函数的方式,一种是正常的函数返回,使用return指令;另外一种是抛出异常。不管使用哪种方式,都会导致栈帧被弹出。

执行引擎运行的所有的字节码指令都是针对当前栈帧进行操作的。

局部变量表:

JVM字节码之整型iconst、istore、iload指令  局部变量表(Local Variable Table)是一组变量值存储空间,用于存放方法参数和方法内定义的局部变量。

       一个局部变量可以保存一个类型为boolean、byte、char、short、int、float、reference和returnAddress类型的数据。reference类型表示对一个对象实例的引用。

从istore_1开始

istore_0 = this

操作数栈

操作数栈(Operand Stack)也常称为操作栈,它是一个后入先出栈(LIFO)。同局部变量表一样,操作数栈的最大深度也在编译的时候写入到方法的Code属性的max_stacks数据项中
当一个方法刚刚开始执行时,其操作数栈是空的,随着方法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。一个完整的方法执行期间往往包含多个这样出栈/入栈的过程。
iconst从0开始

当int取值 -128~127 时,JVM采用 bipush 指令将常量压入栈中

iload:加载局部变量;非静态方法从iload_1开始的,默认第iload_0是this,静态方法iload_0

动态连接

每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking)。在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Reference)保存在class文件的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。

方法返回地址:

种情景:一是正常执行完成后退出,二是出现未处理的以长,非正常退出。无论哪种退出方式,方法退出后都会返回该方法的调用位置。方法正常退出时,调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。

2.Java 对象的创建过程(五步,建议能默写出来并且要知道每一步虚拟机做了什么)

分别是: 加载--验证--准备--解析--初始化

 1>加载:
        加载有两种情况,①当遇到new关键字,或者static关键字的时候就会发生(他们对应着对应的指令)如果在常量池中找不到对应符号引用时,就会发生加载 ,②动态加载,当用反射方法(如class.forName(“类名”)),如果发现没有初始化,则要进行初始化。(注:加载的时候发现父类没有被加载,则要先加载父类)

 2> 验证:
        这一阶段的目的是确保class文件的字节流中包含的信息符合当前虚拟机的要求,并不会危害虚拟机自身的安全(虽然编译器会严格的检查java代码并生成class文件,但是class文件不一定都是通过编译器编译,然后加载进来的,因为虚拟机获取class文件字节流的方式有可能是从网络上来的,者难免不会存在有人恶意修改而造成系统崩溃的问题,class文件其实也可以手写16进制,因此这是必要的)

3>准备:
        该阶段就是为对象分派内存空间,然后初始化类中的属性变量,但是该初始化只是按照系统的意愿进行初始化,也就是初始化时都为0或者为null。因此该阶段的初始化和我们常说初始化阶段的初始化时不一样的

4>解析:
        解析就是虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用其实就是class文件常量池中的各种引用,他们按照一定规律指向了对应的类名,或者字段,但是并没有在内存中分配空间,因此符号就理解为一个标示,而用直接指向内存中的地址

5>初始化:
        简单讲就是执行对象的构造函数,给类的静态字段按照程序的意愿进行初始化,注意初始化的顺序。(此处的初始化由两个函数完成,一个是,初始化所有的类变量(静态变量),该函数不会初始化父类变量,还有一个是实例初始化函数,对类中实例对象进行初始化,此时要如果有需要。

3.对象的访问定位的两种方式(句柄和直接指针两种方式)

建立对象就是为了使用对象,我们的Java程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式有虚拟机实现而定,目前主流的访问方式有使用句柄和直接指针两种:

1>句柄: 如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。

直接指针: 如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而 reference 中存储的直接就是对象的地址。

4.如何判断对象是否死亡(两种方法)。

有以下两种算法判断对象实例是否死亡:
1、引用计数算法:给每个对象添加一个引用计数器,当有对象引用时加1,当引用失效时减1,任何引用计数器为0的对象实例就是不可能再被使用的——对象实例死亡。但它无法解决对象相互引用的情况。
2、可达性分析算法:通过一系列被称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则说明此对象不能再被使用——对象实例已死亡。可作为GC Roots的对象包括:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区常量引用的对象,本地方法栈中本地方法引用的对象。


5.简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好  处)。

1.强引用

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfM moryError错误,使程序异常终止,也不会靠随意回收具有强引用 对象来解决内存不足的问题。

2.软引用

软引用是用来描述一些还有用但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。

3.弱引用

弱引用也是用来描述非必须对象的,他的强度比软引用更弱一些,被弱引用关联的对象,在垃圾回收时,如果这个对象只被弱引用关联(没有任何强引用关联他),那么这个对象就会被回收。

4.虚引用

一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象的实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。虚引用和弱引用对关联对象的回收都不会产生影响,如果只有虚引用活着弱引用关联着对象,那么这个对象就会被回收。它们的不同之处在于弱引用的get方法,虚引用的get方法始终返回null,弱引用可以使用ReferenceQueue,虚引用必须配合ReferenceQueue使用。


6.如何判断一个常量是废弃常量

引用计数器,可达性分析算法

7.如何判断一个类是无用的类

1》所有实例都被回收;2》加载该类的classloader被回收;3》该类对象没有在任何地方引用
8.垃圾收集有哪些算法,各自的特点?待续

9.HotSpot为什么要分为新生代和老年代?

10.常见的垃圾回收器有那些?
11.介绍一下CMS,G1收集器。
12.Minor Gc和Full GC 有什么不同呢?
拓展问题:
1.String类和常量池
2.8种基本类型的包装类和常量池

栈FILO 现金后出 fist in last out

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

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

相关文章

嵌入式Linux驱动开发 04:基于设备树的驱动开发

文章目录 目的基础说明开发准备设备树调整驱动程序与测试总结设备树文件内容 目的 前面文章 《嵌入式Linux驱动开发 03:平台(platform)总线驱动模型》 引入了资源和驱动分离的概念,这篇文章将在前面基础上更进一步,引…

内网或者没有公网IP,如何轻松通过公网访问?

很多网友都面临一个普遍的问题,就是作为家庭宽带用户没有公网IP,即没有IP v4的公网IP,我们天天盼着有一天IP v6能够普及。IP v4一共有2的32次方个地址,约43亿,中国仅为7000多万个,IP v6一共有2的128次方个地…

MySQL8.0版本在CentOS系统的配置教程

1.安装MySQL MySQL安装完成后,会自动配置为名称叫做:mysqld的服务,可以被systemctl所管理,我们在进行系统的配置时,主要修改root密码和允许root远程登录。 # 通过grep命令,在/var/log/mysqld.log文件中&a…

iOS开发:__weak __strong解决Block嵌套

Block使用会存在循环引用的问题,多个Block嵌套使用的情况更复杂,还会出现对象nil的问题。 为什么会循环引用? 现在iOS开发都是在ARC引用计数管理模式下的,参考另一篇文章《Block底层原理》,我们知道Block访问外部变量…

文心一言 VS 讯飞星火 VS chatgpt (29)-- 算法导论5.2 1题

一、在 HIRE-ASSISTANT 中,假设应聘者以随机顺序出现,你正好雇用一次的概率是多少?正好雇用 n 次的概率是多少? 文心一言: 讯飞星火: 华为盘古: 在HIRE-ASSISTANT中,假设应聘者以随机顺序出现…

仙境传说RO:从零开始编译服务端开发环境搭建安装Pandas-rAthena

仙境传说RO:从零开始编译服务端开发环境搭建安装Pandas-rAthena 大家好,我是艾西今天跟大家分享下仙境传说RO 开发脚本环境搭建。 Pandas是RO的模拟器实际上是开源的一个叫做rAthenna的一个开源项目 准备工具: Git 2.25.0 Setup建议放入D盘…

【消息队列】| 队列的优势介绍及应用场景

目录 🦁 前言🦁 那么MQ的优势在哪里?🦁 应用场景🦁 最后 🦁 前言 消息队列:MQ全称Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于系统…

javaweb课程设计——商城项目

前言: 👏作者简介:我是笑霸final,一名热爱技术的在校学生。 📝个人主页:个人主页1 || 笑霸final的主页2 📕系列专栏:项目专栏 📧如果文章知识点有错误的地方,…

2.2 动态范围的常用计算方法

1. 动态范围的常用计算方法 动态范围(Dynamic Range)指的是输入数据中数值的范围,计算动态范围是为了确定量化时使用的比特位数(还是抽象😂)。个人理解:考虑到输入数据可能存在数据分布不均,即有些数据偏离过大。而过大的偏离值,会…

Ansys Zemax | NSC 非序列矢高图用户分析

本文介绍如何使用 NSC 矢高图用户分析功能在非序列模式下测量和显示对象的矢高。了解此功能的基础知识,包括如何设置复杂 CAD 零件的文件以获取特定面的矢高值。(联系我们获取文章附件) 介绍 OptocStudio 的序列模式具有表面矢高分析功能&…

硬件系统工程师宝典(28)-----关于LDO,应该知道的事

各位同学大家好,欢迎继续做客电子工程学习圈,今天我们继续来讲这本书,硬件系统工程师宝典。上篇我们说到BJT配合MOSFET控制电源开关的四种电路以及MOSFET的均流电路。今天我们来讲讲LDO的应用分析。 LDO的结构 LDO(Low Dropout R…

Linux内核源码的配置和编译

目录 配置交叉编译工具链 读README 配置内核源码支持当前的硬件平台 驱动配置 内核编译 编译: 问题: 解决问题的方法: 测试内核 配置交叉编译工具链 打开内核源码顶层目录的Makefile, hqUbuntu:~/fs6818_uboot/kernel-3.4.39$ vi …

IP地址、子网划分

目录 一、IP地址1.IP地址表示2.分类IP地址3.无分类编址 CIDR4.特殊IP地址 二、子网划分1.子网、子网掩码、子网划分VLSM2.网络地址、广播地址3.示例1:等分为两个子网3.1 划分前:3.2 划分后: 4.示例2:等分为四个子网3.1 划分前&…

五种经典IO模型详解

目录 同步和异步同步阻塞IO模型基本概念应用场景优缺点 同步非阻塞IO模型基本概念应用场景优缺点 IO多路复用模型信号驱动IO模型回顾复习1.信号2.产生信号的条件3.可重入函数4.为什么中断处理函数不能直接调用不可重入函数5.如何写出可重入的函数 基本概念应用场景优缺点 异步I…

【操作系统】 1、计算机系统概述

1.1 操作系统的基本概念 从操作系统的角度上来划分计算机体系结构: 这里注意一点: 编译器属于应用程序。 操作系统:是指控制和管理计算机系统的 硬件 和软件 资源,合理的组织、调度计算机的工作与资源分配,进而为用…

对象数组练习案例

定义一个长度为3的数组,数组存储1~3名学生对象作为初始数据,学生对象的学号,姓名各不相同。 * 学生的属性:学号、姓名、年龄 * 要求1:再次添加一个学生对象,并在添加的时候进行学号的唯一性判断 * 要求2&am…

Nautilus Chain:我们将支持EIP6969

在今年 5 月初,以太坊核心开发者、Slingshot 的 CTO zkCole 提出了一个通用的协议标准 EIP-6969 ,其旨在实现合约保护收入(在以太坊 L2 上引入 / 标准化 CSR ),该提案可以看作是之前 EIP-1559的改进版,并在…

Record类浅喽一眼~

Record类的一点小概念嗷。 一. 基本使用 java19 的新特性: 我们先构造一个student的Record类. 默认构造几个属性. public record Student(Integer id,String name, String email,Integer age) {} 然后简单搞一点例子 public static void main(String[] args) { St…

2023年上半年系统分析师上午真题及答案解析

1.信息系统的构成包括( )。 A.计算机硬件、计算机软件、网络和通信设备、系统分析人员、系统设计人员、系统开发人员 B.计算机硬件、计算机软件、系统分析人员、系统设计人员、系统开发人员 C.计算机硬件、计算机软件、系统设计人员、系统开发人员、信息用户 D.计算机硬件…

【C++】类和对象——友元函数和友元类的概念、初始化列表、explicit关键字、static成员

文章目录 1.友元函数和友元类的概念1.1友元函数1.2友元类 2.构造函数知识补充2.1初始化列表2.2explicit关键字 3.static成员3.1static成员概念3.2static成员特性 1.友元函数和友元类的概念 在C中,友元函数和友元类是指允许非成员函数或非成员类访问某个类中的私有成…