面试题-Java虚拟机
前言
Java虚拟机部分的题目,是我根据Java Guide的面试突击版本V3.0再整理出来的,其中,我选择了一些比较重要的问题,并重新做出相应回答,并添加了一些比较重要的问题,希望对大家起到一定的帮助。
系列文章:
JVM-自动内存管理
说说虚拟机的运行时内存区域都有什么?
JDK1.8以前
线程私有的:程序计数器、java虚拟机栈和本地方法栈
线程共享的:方法区 和 堆
JDK1.8之后
线程私有的:程序计数器、java虚拟机栈和本地方法栈
线程共享的:元数据区 和 堆
其中,方法区修改为元数据区,并且把元数据区移动到了直接内存里
java虚拟机栈可能会抛出什么错误?
- 如果虚拟机栈不可动态扩展,当线程请求的栈深度大于虚拟机允许的最大深度,会抛出StackOverFlowError;
- 如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,会抛OutofMemoryError
谈谈对象的创建过程?(重要)
下面讨论仅限于HotSpot虚拟机的对象创建过程。
- 首先,虚拟机会检测new指令对应的参数在常量池中是否可以找到对应的符号引用
- 然后,检查这个类是否已经被解析、加载、初始化过了,如果没有,首先启动类的加载过程
- 加载完毕后,就可以确定这个类的内存大小,开始分配内存
- 如果垃圾收集算法带整理功能,会使用指针碰撞算法分配
- 如果垃圾收集算法不带整理功能,会使用空闲列表分配
- 分配内存的过程中,还需要考虑线程安全的问题,可以使用两种方式解决
- CAS和不断重试
- TLAB:针对每个线程预先分配内存,线程在自己的内存区域分配即可。
- 分配完毕内存后,会给每个字段设置零值,然后设置对象头相关参数
- 最后调用init方法
你了解对象的内存布局吗?大致谈谈(对象在虚拟机中的数据结构)
对象在虚拟机中的数据结构包括三大部分:
- 对象头:对象头中包括了哈希吗、GC年龄、锁信息等等
- 实例数据:程序中定义的各种数据内容
- 对齐填充:HotSpot管理系统要求对象的起始地址必须为8字节的整数倍,所以在不满足时会进行对齐填充。
对象的访问定位有哪两种⽅式?
一种是通过句柄访问,另一种是通过直接指针访问
使用句柄访问的优点是,无论对象如何移动,栈中保存的reference是稳定的。
使用直接内存方式的优点是,和使用句柄方式相比,节省了一次指针定位的开销,可以更快的定位数据。

上面介绍了虚拟机运行时的内存区域布局,对象在内存中的数据结构,总结来看是介绍了产生对象的相关知识,既然产生了对象,在不使用时就需要垃圾收集,所以下面的问题要进入垃圾收集相关的主题了。
如何判断对象已死
有两种方式可以判断对象已死:
引用计数法:无法解决循环引用的问题
可达性分析:由GCROOT开始,向下搜索,搜索走过的路径叫做引用链。当一个对象到GCROOT没有引用链相连时,就证明这个对象是不可用的。
可以作为GCROOT的对象包含以下几种:
- 栈中引用的对象(包括虚拟机栈和本地方法栈)
- 静态属性引用的对象
- 常量引用的对象

谈谈对finalize方法的理解
第6个问题分析了如何判断对象已死,假如满足已死条件,虚拟机不会立即判对象死亡,而是先判断是否需要调用finalize方法。以下两个条件满足,JVM就不会调用finalize方法
- 没有覆盖finalize方法
- 已经调用过一次了
finalize方法中对象可以拯救自己,但因为运行代价高昂,不确定性大,所以不建议使用。
方法区中会进行垃圾回收吗?
在虚拟机规范中说过,虚拟机实现中可以不实现方法区的垃圾收集。在HotSpot虚拟机中,可以通过配置参数来开启或关闭方法区的垃圾收集。
- -Xnoclassgc:关闭类卸载
- -verbose:class:观察类类加载和卸载信息
方法区回收的主要是什么类型的数据?如何判断已死?
方法区中主要回收两类数据:废弃常量和无用类
如果没有任何一个引用引用了这个常量,该常量为废弃常量,可以被回收
无用类的判定条件比较多:
- 类产生的实例都被回收
- 加载类的加载器也被回收
- 类的class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
你了解强引用、软引用、弱引用、虚引用吗?(重要)
问题6中讨论过如何判断对象已死,判断方式都离不开引用。在上面的讨论中,隐含了一个条件,就是对象要么是被引用,要么没有被引用。在JDK1.2以后,Java对引用的概念进行了扩充。引入了强引用、软引用、弱引用、虚引用的概念。
- 强引用:如果引用存在,在内存不足时也不会回收。在代码中写的变量都是强引用。
- 软引用:如果只有软引用存在,那么在内存不足时,才会把只有软引用的对象进行回收
- 弱引用:如果只有弱引用存在,每次垃圾回收时,都会去尝试回收
- 虚引用:简单理解,虚引用完全不影响垃圾回收,它最大的作用是在回收时收到通知。
上面的内容主要介绍了如何判断对象是否已死,具体如何清理已死对象?清理以后是否需要整理?都是下面的垃圾收集算法和实现垃圾收集算法的垃圾收集器考虑的内容。
垃圾收集有哪些算法,各⾃的特点是什么?
- 标记-清除算法:标记后,直接清除。缺点:容易产生空间碎片,影响后续分配大对象
- 复制算法:把内存一分为二,每次只在一半分配内存,如果需要垃圾收集,把存活的对象复制到另半边,直接清理原半区即可。缺点:大部分对象都存活时,复制效率过低;内存一分为二,空间利用率差。
- 标记整理算法:对标记清除算法的改进,标记以后,不直接清除,而是把存活的对象移动到一侧,清理端边界即可。
- 分代算法:根据对象存活周期的不同把内存区域划分为多块,每块使用不同的收集算法。
- 新生代:复制算法
- 老年代:标记-清除 或 标记整理算法
新生代的垃圾收集器有哪些?分别有什么特点?
新生代垃圾收集器有三种:
- serial: 单线程,stop the world,标记整理算法,适合运行在单CPU上的程序。
- parnew:多线程,stop the world。适合运行在多CPU上的程序,比serial提供了更多的可配置参数
- parallel scavenger:多线程,stop the world。更关注吞吐量,还提供自适应参数。
注:关注吞吐量和关注延迟的区别
吞吐量的定义:系统运行用户代码的时间/(运行用户代码的时间+垃圾收集时间)
serial和parnew关注的是每次收集的低延迟,但低延迟可能带来更多的收集次数,在计算吞吐量时,可能更小。
比如:同样的代码,原来每10秒手机一次,停顿100ms;为了更低的延迟,调小了新生代,现在每5秒停顿一次,每次收集70ms,看起来每次的延迟降低了,但是系统整体的吞吐量却降低了。
老年代的垃圾收集器有哪些?分别有什么特点?
老年代的垃圾收集器有四种:
- serial old: 单线程,stop the world,标记整理算法,适合运行在单CPU上的程序。
- parallel old:serial的多线程版本,stop the world。标记整理算法。
- CMS:标记清除算法,以最小延迟时间为目标。
- G1:最前沿的收集器,可以以极高概率满足GC停顿时间,还具备高吞吐量的特征
CMS收集器的工作流程是怎样的?
- 初始标记:stop the world。只标记GCROOT直接关联的对象,停顿时间很短
- 并发标记:GC ROOT tracing的过程,可以与用户线程一起工作
- 重新标记:stop the world。修正并发标记阶段因为用户线程导致标记出现变化的部分
- 并发清除
CMS收集器的缺点?以及如何解决?
- 空间碎片问题(整理一下):因为CMS使用标记清除算法,所以会产生空间碎片。CMS提供一个参数,可以控制执行N次GC后需要做一次空间压缩。 -XX:CMSFullGCsBeforeCompaction=n
- 浮动垃圾问题(用户线程并发不可避免的产生浮动垃圾):CMS在运行过程中,用户线程也在运行,在所有的标记阶段结束以后,产生的垃圾叫做浮动垃圾,这些垃圾只能等到下一次GC才会收集。
- Concurrent Mode Failure问题(用户线程并发,所以需要考虑预留一些空间):因为并发执行,所以老年代中需要预留一些空间给用户线程使用。用于空间担保和正常进阶到老年代的数据。当这个预分配的空间不足时,就会触发Concurrent Mode Failure问题,这时JVM会使用serial收集器来收集,性能反而降低。可以调优这个空间比例参数:-XX:CMSInitiatingOccupancyFraction(这个参数主要关注老年代使用率达到多少比例时,触发垃圾回收,所以为了保证用户线程正常使用,要预留小一点的这个比例,也就是较小的比例会触发CMS垃圾回收)
- cpu资源敏感:CMS默认启动的线程数为 (CPU数量+3)/4,当cpu数量较少时,会使用更大比例的线程用于垃圾回收,间接影响总吞吐量。
G1收集器的原理?(待完成)
上面集中讨论了垃圾回收具体的实现,基于此,可以讨论自动内存管理的另一方面--对象分配的原则。
说⼀下堆内存中对象的分配的基本策略
当垃圾收集算法为分代算法时,对象分配策略如下:

JVM-执行子系统
在JVM-自动内存管理 的 “谈谈对象的创建过程?”的这个问题中,在创建对象的第二步:检查这个类是否已经被解析、加载、初始化过了,如果没有,首先启动类的加载过程。我们提过类的加载过程,这部分内容会更加详细的讨论类的加载。
说说类加载的过程?
类加载的过程主要分为三大步骤:
加载
- 通过类的全限定名获取二进制字节流
- 将二进制字节流代表的静态数据结构转换为运行时数据结构
- 生成一个代表这个类的Class对象,作为运行时数据结构的访问入口
连接
验证:加载Class之前的文件格式验证;加载为运行时数据区后的相关语义验证
准备:为类变量分配内存和设置零值的过程
解析:加载为运行时数据结构以后,还需要把符号引用解析为直接引用
注:符号引用简单理解就是常量;直接引用就是具体的内存地址或者句柄。在上面第五个问题:对象的访问定位有哪两种⽅式? 中介绍过句柄和直接指针的相关概念
初始化:执行类的clinit方法,clinit方法包括静态变量的赋值和static代码块中的赋值
类加载的时机?
JVM虚拟机规范中没有明确规定加载开始的时机,而是规定了几种场景,在这些场景下下必须立即执行初始化步骤,这些场景称为类的主动引用。
- new 创建实例;设置或读取静态字段;调用类的静态方法(通过子类调用父类的静态字段,只一定会初始化父类;final修饰的常量不会触发初始化)
- 反射调用
- 父类还未初始化,会先初始化父类
- 虚拟机启动后第一个执行的主类
- MethodHandle实例解析为 设置或读取静态字段;调用类的静态方法对应的字节码指令(和第一种情况一样)
其他所有的情况称之为被动调用,不会触发类的初始化
类加载器有什么作用?java中有哪些类加载器?
在整个类加载的过程中,类加载器只负责 加载 阶段的 通过类的全限定名获取二进制字节流 这个阶段。
java中有三种预定义的加载器:
- 启动类加载器:负责加载lib下的相关jar,由c++实现在虚拟机内部
- 扩展类加载器:负责加载lib/ext下的jar,由java实现
- 应用类加载器:负责加载用户的class path下的jar,由java实现
简单说说双亲委派模型?
双亲委派模型的工作过程是:如果类没加载过,先委派给父类加载,如果父类加载失败,再由子类加载。
双亲委派模型可以保证java程序的稳定运行,比如java.lang.Object类,无论哪一个类加载器要加载这个类,最终都会委派给启动类加载器来加载,保证核心类的唯一性。
面试题-Java虚拟机的更多相关文章
- 面试题——Java虚拟机
一.运行时数据区域 Java虚拟机在执行Java程序的时候会把它所管理的内存划分为若干个不同的数据区域,这些区域各有用途: 程序计数器:(线程私有的) 程序计数器是一块较小的内存,可以看作是当前线程所 ...
- Java 虚拟机面试题全面解析(干货)
Java 虚拟机面试题全面解析(干货) JDK 是什么 JRE 是什么 Java历史版本的特性 Java Version SE 50 Java Version SE 6 Java Version SE ...
- 从一道面试题深入了解java虚拟机内存结构
记得刚大学毕业时,为了应付面试,疯狂的在网上刷JAVA的面试题,很多都靠死记硬背.其中有道面试题,给我的印象非常之深刻,有个大厂的面试官,顺着这道题目,一直往下问,问到java虚拟机的知识,最后把我给 ...
- Java 虚拟机部分面试题
Java虚拟机部分的面试内容包括三部分:GC.类加载机制以及内存 Java内存区域 JVM内存分为哪几部分,这些部分分别都存储哪些数据? 线程隔离的数据区:程序计数器.Java虚拟机栈.本地方法栈. ...
- Java面试题之Java虚拟机垃圾回收
JVM的垃圾回收机制,在内存充足的情况下,除非你显式的调用System.gc(),否则不会进行垃圾回收:在内存充足的情况下垃圾回收会自动运行. 一.引用计数算法 1.定义:引用计数算法会给对象添加一个 ...
- Java面试题(三)--虚拟机
1 内存结构 1.简述一下JVM的内存结构?(高频) JVM在执行Java程序时,会把它管理的内存划分为若干个的区域,每个区域都有自己的用途和创建销毁时间.如下图所示,可以分为两大部分,线程私有区和共 ...
- 深入理解java虚拟机(1)------内存区域与内存溢出
在C++领域,关于C++的内存存储,结构等等,有一本书:深度探索C++对象模型,讲解的非常透彻. 而Java确把这一工作交给了虚拟机来处理. 我们首先来看看关于内存的问题. 1.问题: 1)java ...
- 谈谈java虚拟机
本文可作为北京圣思元深入java虚拟机的课堂笔记. 先看一个令人dan teng的面试题 public class Singleton { public static Singleton s=new ...
- 《深入理解Java虚拟机》-----第8章 虚拟机字节码执行引擎——Java高级开发必须懂的
概述 执行引擎是Java虚拟机最核心的组成部分之一.“虚拟机”是一个相对于“物理机”的概念 ,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的,而 ...
- (转)JVM——Java虚拟机架构
背景:最近开始忙着找工作了,把需要储备的知识再整理总结一遍!关于JVM的总结,是转自下面的连接.结合<深入理解java虚拟机>,看起来有更清晰的认识. 转载自:http://blog.cs ...
随机推荐
- ESP8266 wifi模块+CH340烧录板安装使用运行教程
硬件准备 ESP8266-01S 模块 USB转TTL烧录板(CH340) LED灯(建议使用3.3V LED) 220Ω电阻(LED限流) 面包板和跳线若干 我的是这样的不用接线,其他的参考 ESP ...
- Solution -「CF 808E」Selling Souvenirs
\(\mathscr{Description}\) Link. 01 背包. 物品种类 \(n\le10^5\),背包容量 \(m\le3\times10^5\),单个物品体积 \(w\i ...
- 深入浅出:Agent如何调用工具——从OpenAI Function Call到CrewAI框架
深入浅出:Agent如何调用工具--从OpenAI Function Call到CrewAI框架 嗨,大家好!作为一个喜欢折腾AI新技术的算法攻城狮,最近又学习了一些Agent工作流调用工具的文章,学 ...
- mysql数据库指定ip远程访问(设置远程连接),赋权操作
mysql数据库指定ip远程访问(设置远程连接) 远程访问mysql报错,ip不允许链接的情况:错误号码1045Access denied for user '用户名' @'数据库地址' (using ...
- Kotlin:【Map集合】集合创建、集合遍历、元素增加
to本身是一个函数
- 基于deepseek模型知识库,Cherry Studio和AnythingLLM使用效果对比
基于deepseek模型知识库,Cherry Studio和AnythingLLM使用效果对比 目 录 1. 使用效果对比基础 2. Cherry Studio和Any ...
- 三种方式从jdbc中获取数据库表字段信息
一.整体代码 1.method1:执行select语句获取,select * from dims where 1 = 2 2.method2:执行show create table获取,show cr ...
- Luogu P9055 [集训队互测 2021] 数列重排 题解 [ 紫 ] [ 构造 ] [ 数学 ]
数列重排:差点就场切的神仙构造,最后一步想假了,导致我模拟赛荣获 25+5+0 的好成绩! 这题部分分很有启发性,跟着一步一步打基本能想到正解的构造,但也有可能想偏部分分的意思,想假策略. 构造 先看 ...
- 微信扫码登录授权过程中state字段的用法
问题描述 最近在实现微信扫码登录这一块,然后看到state字段上面说是可以防csrf攻击 那么现在假设一个用户扫完码后由于某些原因扫码后的响应还没到,但是该平台的回调url已被窃取,然后被人设置到某个 ...
- 傻妞教程——对接mongoDB使用返佣系统
使用docker安装mongo 1.安装 1.1 拉取mongo镜像 docker pull mongo:4.4 1.2 创建mongo数据持久化目录 mkdir -p /docker_volume/ ...