JVM基础面试题

news2024/11/25 5:27:12

JDK、JRE、JVM的关系

JVM
Java虚拟机,它只识别.class类型文件,它能将class文件中的字节码指令进行识别并调用操作系统向上的API完成动作。
JRE
Java运行时环境。它主要包含两部分:Jvm的标准实现和Java的一些基本类库。相对于JVM来说,JRE多出来一部分Java类库。
JDK
Java开发工具包。包括整个Java开发的核心,它集成了JRE和一些好用的小工具,如:javac.exe、java.exe、jre.exe等。
三者的关系:一层层的嵌套关系,JDK>JRE>JVM。

JVM内存模型以及分区情况和作用

如图所示:

黄色部分线程线程共享,蓝色部分线程私有。

方法区

存储类加载器的类信息,常量,静态变量等数据。

存放实例对象,所有的对象(不包括直接内存对象、符合逃逸分析的对象和)和数组都在堆内存分配,JVM所管理的内存中最大的一块区域。

Java方法执行的内存模型:存储局部变量表,操作数栈,动态链接,方法出口等信息。生命周期与线程相同

本地方法栈

作用与虚拟机栈类似。不同点本地方法栈为native方法执行服务,虚拟机栈为虚拟机执行的java方法服务。

程序计数器

当前线程所执行的行号指示器,是JVM内存区域最小的一块区域。执行字节码工作时就是利用程序计数器来选取下一条需要执行的字节码指令。

JVM对象创建流程

整体流程图如下

  1. 虚拟机遇到new指令,首先去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并检查这个符号引用的类是否已被加载,解析,初始化。
  2. 如果类已经加载直接分配内存,如果未加载,则先进行类的加载。
  3. 类加载检查通过之后,对新对象进行内存分配。
  4. 对象生成需要的内存大小在加载完成后便可完全确定,为对象分配空间等同于从Java堆内存中划分出一块确定大小的内存
  5. 内存大小划分分为两种情况:JVM内存是规整的(使用和未使用的内存分别放一边,中间放一个指针作为分界点指示器,这样分配就很简单,只需要将指针向空闲空间那边挪动与对象大小相同的距离,这就是“指针碰撞”)。JVM内存不是规整的(使用内存和未使用内存相互交错,这时候我们需要维护一张表,用于记录哪些内存可用,在分配时从列表中找到一个足够大的空间划分给对象,并更新到这张表中)
  6. JVM将内存空间初始化为0值,如果使用TLAB,就可以在TLAB分配的时候进行工作。
  7. JVM对对象进行必要设置。
  8. 执行完以上步骤之后从JVM来看一个对象基本上就完成了,但从Java程序代码绝对来看,对象创建才刚刚开始,需要执行init方法,按照程序中设定的初始化操作初始化,这时候一个真正的程序对象才生成了。

垃圾回收算法有哪些,他们的优缺点又是什么?

常见的垃圾回收算法有:

标记清除算法,复制算法,标记整理算法和分代收集算法

标记清除算法

包含两个阶段,标记和清除

标记阶段:确认所有需要回收的对象并做好标记

清除阶段:将标记节点标记不可用的对象清除

缺点:

标记和清除的效率都不高,会产生大量的碎片,导致频繁的回收。

复制算法

内存分为相等大小的两块,每次使用其中一块。当垃圾回收的时候把存活对象复制到另一块上,然后把这块内存整个清理掉

缺点:

浪费额外的内存作为复制区,当对象存活率较高时,复制算法效率会下降。

标记整理算法

不是把存活的对象复制到另一块内存,而是把存活对象往内存的一端移动,然后直接回收边界以外内存。

缺点:

算法复杂度大,执行步骤较多。

分代收集算法

目前大多数JVM垃圾收集器采用的算法,根据对象存活的生命周期将内存划分为若干不同的区域,一般情况将堆区划分为新生代、老年代和永久代。

老年代的特点是每次垃圾收集时只有少量对象被回收,而新生代的特点是每次垃圾收集会有大量对象被回收,那么根据不同代的特点采取最合适的收集算法。

如图:

Young区:存放新创建的对象,对象生命周期非常短,几乎用完可以立即回收,也叫eden区

Tenured:Young区经过多次回收后存活下来的对象将被移到改区,也称old区

Permanent:永久代,主要存放加载类的信息,生命周期长,几乎不会被回收。

缺点:

算法复杂,步骤较多。

简单介绍一下什么是类加载机制

Class文件由类加载器装载后,在JVM中形成一份描述Class结构的元信息对象,通过该元信息对象可以获取Class结构信息,如构造函数,方法,属性等。

虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java对象。

类加载过程是什么,简单描述下每个步骤?

包括:加载,验证,准备,解析,初始化

加载

查找并加载类的二进制数据

加载是类加载的第一个阶段,需要完成三件事情:

通过类的全限定名来获取其定义的二进制字节流

将字节流所代表的静态存储结构转换为方法区的运行时数据结构

在Java堆中生成一个代表这个类的java.lang.class对象,作为方法区中这些数据的访问入口。

验证

确保被加载的类的正确性

确保class文件的字节流中包含的信息符合当前虚拟机的规范,并且不会损害虚拟机自身的安全,包含四个验证动作:文件格式验证,元数据验证,字节码验证,符号引用验证。

准备

为静态变量分配内存,并将其初始化为默认值

正式为类的静态变量分配内存并设置类变量初始值阶段,这些内存都将在方法区中分配。

解析

符号引用转为直接引用

虚拟机将常量池中的符号引用替换为直接引用的过程,解析动作主要针对类或者接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。

初始化

类变量进行初始化

为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始。

jvm类加载器有几种?分别作用是什么?

启动类加载器,扩展类加载器,程序类加载器

启动类加载器

引导类装入器是本地代码实现的类装入器,他负责将java_runtime_home/lib下面的类库加载到内存中。由于引导类加载器设计到虚拟机本地实现细节,开发者无法知己获取到启动类加载器的引用。

标准扩展类加载器

负责将java_runtime_home/lib/ext或者由系统变量java.ext.dir指定位置中的类库加载到内存中,程序员可以直接使用标准扩展类加载器

程序类加载器

负责加载用户路径classpath上的类库

双亲委派模式,有什么作用?

当一个类加载器需要加载一个类时,并不会立即自己去记载,而是首先委派给父类加载器去加载,父类加载器加载不了再给父类的父类去加载,一层一层往上委托,直到顶层加载器(启动类加载器),如果父类加载器反馈无法加载那么类加器才会自己去加载。(如下图所示)

作用:

1) 防止重复加载类。在JVM中,要唯一确定一个对象,是由类加载器和全类名两者共同确定的,考虑到各层级的类加载器之间仍然由重叠的类资源加载区域,通过向上抛的方式可以避免一个类被多个不同的类加载器加载,从而形成重复加载。

2) 安全。例如读者朋友定义了一个名为java.lang.Integer的类,而该类在核心库中也存在,借用双亲委派的机制,我们就能有效防止该自定义的同名类被加载,从而保护了平台的安全性。

怎么打破双亲委派模型?

打破双亲委派机制则不仅 要继承 ClassLoader 类,还要 重写 loadClass fifindClass 方法。

JVM垃圾收集器有哪些?

主要分为以下七种,如图:

每种垃圾收集器之间有连线,表示他们可以搭配使用。

Serial收集器

Serial 是一款用于新生代的单线程收集器,采用复制算法进行垃圾收集。Serial 进行垃圾收集时,不仅只用一条线程执行垃圾收集工作,它在收集的同时,所有的用户线程必须暂停(Stop The World)。

就比如妈妈在家打扫卫生的时候,肯定不会边打扫边让儿子往地上乱扔纸屑,否则一边制造垃圾,一遍清理垃圾,这活啥时候也干不完。

如下是 Serial 收集器和 Serial Old 收集器结合进行垃圾收集的示意图,当用户线程都执行到安全点时,所有线程暂停执行,Serial 收集器以单线程,采用复制算法进行垃圾收集工作,收集完之后,用户线程继续开始执行。

ParNew 收集器

ParNew 就是一个 Serial 的多线程版本,其它与Serial并无区别。ParNew 在单核 CPU 环境并不会比 Serial 收集器达到更好的效果,它默认开启的收集线程数和 CPU 数量一致,可以通过 -XX:ParallelGCThreads 来设置垃圾收集的线程数。

如下是 ParNew 收集器和 Serial Old 收集器结合进行垃圾收集的示意图,当用户线程都执行到安全点时,所有线程暂停执行,ParNew 收集器以多线程,采用复制算法进行垃圾收集工作,收集完之后,用户线程继续开始执行。

Parallel Scavenge 收集器

Parallel Scavenge 收集器也是一个新生代垃圾收集器,同样使用复制算法,也是一个多线程的垃
圾收集器,它重点关注的是程序达到一个可控制的吞吐量(Thoughput,CPU 用于运行用户代码
的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),
高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而
不需要太多交互的任务。自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个
重要区别。

Serial Old 收集器

Serial Old 收集器是 Serial 的老年代版本,同样是一个单线程收集器,采用标记-整理算法。 这个收集器也主要是 运行在 Client 默认的 java 虚拟机默认的年老代垃圾收集器
在 Server 模式下,主要有两个用途:
1. 在 JDK1.5 之前版本中与新生代的 Parallel Scavenge 收集器搭配使用。
2. 作为年老代中使用 CMS 收集器的后备垃圾收集方案。

Parallel Old 收集器

Parallel Old 收集器是 Parallel Scavenge 的老年代版本,是一个多线程收集器,采用标记-整理算法。可以与 Parallel Scavenge 收集器搭配,可以充分利用多核 CPU 的计算能力。

CMS收集器

Concurrent mark sweep(CMS)收集器是一种年老代垃圾收集器,其最 主要目标是获取最短垃圾
回收停顿时间, 和其他年老代使用标记-整理算法不同,它使用 多线程的标记-清除算法
最短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验。
CMS 工作机制相比其他的垃圾收集器来说更复杂,整个过程分为以下 4 个阶段:
初始标记
只是标记一下 GC Roots 能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。
微信公众号:Java架构师进阶编程 13/04/2018
Page 34 of 283
并发标记
进行 GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。
重新标记
为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记
记录,仍然需要暂停所有的工作线程。
并发清除
清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并
发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作, 所以总体上来看
CMS 收集器的内存回收和用户线程是一起并发地执行。
CMS 收集器工作过程

G1 收集器

G1 收集器是 jdk1.7 才正式引用的商用收集器,现在已经成为 jdk9 默认的收集器。前面几款收集器收集的范围都是新生代或者老年代,G1 进行垃圾收集的范围是整个堆内存,它采用 “ 化整为零 ” 的思路,把整个堆内存划分为多个大小相等的独立区域(Region),在 G1 收集器中还保留着新生代和老年代的概念,它们分别都是一部分 Region,如下图:

每一个方块就是一个区域,每个区域可能是 Eden、Survivor、老年代,每种区域的数量也不一定。JVM 启动时会自动设置每个区域的大小(1M ~ 32M,必须是 2 的次幂),最多可以设置 2048 个区域(即支持的最大堆内存为 32M*2048 = 64G),假如设置 -Xmx8g -Xms8g,则每个区域大小为 8g/2048=4M。

为了在 GC Roots Tracing 的时候避免扫描全堆,在每个 Region 中,都有一个 Remembered Set 来实时记录该区域内的引用类型数据与其他区域数据的引用关系(在前面的几款分代收集中,新生代、老年代中也有一个 Remembered Set 来实时记录与其他区域的引用关系),在标记时直接参考这些引用关系就可以知道这些对象是否应该被清除,而不用扫描全堆的数据。

G1 收集器可以 “ 建立可预测的停顿时间模型 ”,它维护了一个列表用于记录每个 Region 回收的价值大小(回收后获得的空间大小以及回收所需时间的经验值),这样可以保证 G1 收集器在有限的时间内可以获得最大的回收效率。

如下图所示,G1 收集器收集器收集过程有初始标记、并发标记、最终标记、筛选回收,和 CMS 收集器前几步的收集过程很相似:

① 初始标记:标记出 GC Roots 直接关联的对象,这个阶段速度较快,需要停止用户线程,单线程执行。

② 并发标记:从 GC Root 开始对堆中的对象进行可达新分析,找出存活对象,这个阶段耗时较长,但可以和用户线程并发执行。

③ 最终标记:修正在并发标记阶段引用户程序执行而产生变动的标记记录。

④ 筛选回收:筛选回收阶段会对各个 Region 的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来指定回收计划(用最少的时间来回收包含垃圾最多的区域,这就是 Garbage First 的由来——第一时间清理垃圾最多的区块),这里为了提高回收效率,并没有采用和用户线程并发执行的方式,而是停顿用户线程。

适用场景:要求尽可能可控 GC 停顿时间;内存占用较大的应用。可以用 -XX:+UseG1GC 使用 G1 收集器,jdk9 默认使用 G1 收集器。

对象已死是什么意思

对象不可能在被任何途径使用

判断对象已死的方法有:引用计数法和可达性分析算法

什么情况会栈溢出

  1. 方法创建一个很大的对象,如:List,Array
  2. 是否产生循环调用,死循环
  3. 是否引用了较大的全局变量

Java四种引用类型,强软弱虚

强引用:new出的对象之类的,只要强引用还在永远不会被回收,因此强引用是造成 Java 内存泄漏的主要原因之 一。

软引用:当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中。

弱引用:它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管JVM 的内存空间是否足够,总会回收该对象占用的内存。
虚引用: 它不能单独使用,必须和引用队列联合使用。虚引用的主要作用是跟踪对象被垃圾回收的状态。

Eden和Survivor的分配比例是多少,为什么?

默认比例是8:1
大部分对象朝生夕死。

cpu占用过高排查

top命令查看cpu占用最高程序

top -H -p [java进程id],找到cpu占用较高的线程id

计算java线程id的16进制值,因为后续用jstack看到的线程快照中,线程id为小写十六进制值

(1)可百度在线进制转换

(2)可使用windows自带的计算器,程序员模式,可转换十六进制

(3)Linux可使用命令:printf "%x\n" [线程_id]

使用命令 jstack [java进程pid] | grep [线程id十六进制值] -A 30(-A 30表示向下打印30行)

怎么打出线程栈信息

输入 jps ,获得进程号。
top -Hp pid 获取本进程中所有线程的 CPU 耗时性能
jstack pid 命令查看当前 java 进程的堆栈状态
或者 jstack -l > /tmp/output.txt 把堆栈信息打到一个 txt 文件。

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

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

相关文章

【狂神】SpringMVC笔记(一)之详细版

1.Restful 风格 概念: 实现方式: 使用PathVariable 在url相同的情况下,会根据请求方式的不同来执行不同的方法。 使用RestFull风格的好处:简洁、高效、安全 2、接受请求参数及数据回显 2.1、请求参数 方式一:这里…

无涯教程-JavaScript - TIME函数

描述 TIME函数返回特定时间的十进制数。如果在输入功能之前单元格格式为"常规",则输出格式为日期。 TIME返回的十进制数是一个介于0(零)到0.99988426之间的值,代表从0:00:00(12:00:00 AM)到23:59:59(11:59:59 P.M.)的时间。 语法 TIME (hour, minute, second)争论…

Android studio 调整jar包顺序

第一步:编译jar包,放入lib路径下:如: 第二步:app 目录下build.gradle 中添加 compileOnly files(libs/classes.jar) 第三步:project目录下build.gradle 中添加 allprojects {gradle.projectsEvaluated {t…

十四、MySQL(DCL)如何实现用户的简单管理?配置用户?

1、DCL语句: 要学习DCL语言,就要清楚DCL语言是用来干什么的,首先DCL语言英文全称是Data Control Language,是数据控制语言,主要用来管理数据库用户、控制数据库的访问权限/ 2、DCL的基础语法: (…

SpringBoot项目启动时预加载

SpringBoot项目启动时预加载 Spring Boot是一种流行的Java开发框架,它提供了许多方便的功能来简化应用程序的开发和部署。其中一个常见的需求是在Spring Boot应用程序启动时预加载一些数据或执行一些初始化操作。 1. CommandLineRunner 和 ApplicationRunner Spri…

垃圾回收 - 标记压缩算法

压缩算法是将标记清除算法与复制算法相结合的产物。 1、什么是标记压缩算法 标记压缩算法是由标记阶段和压缩阶段构成。 首先,这里的标记阶段和标记清除算法时提到的标记阶段完全一样。 接下来我们要搜索数次堆来进行压缩。压缩阶段通过数次搜索堆来重新填充活动对…

mtbatisplus

title: mybatis_plus date: 2023-09-03 21:06:27 tags: mybatis_plusreviews 约定大于配置 默认 当我们要使用mybatisplus的时候 我们需要去集成mp提供的BaseMapper public interface UserMapper extends BaseMapper 关于mybatisplus常用注解 注解到类上 TableName(“表…

【c++】只出现一次的数字I II III(三个版本:三道题)

注&#xff1a;看这篇文章之前你需要了解& | ^ << >> 这五个运算符&#xff0c;此外&#xff0c;代码均经过测试运行通过 目录 1、只出现一次的数字I&#xff08;难度&#xff1a;简单&#xff09; 2、只出现一次的数字II&#xff08;难度&#xff1a;中等&a…

Ubuntu之apt-get系列--apt-get安装软件的方法/教程

原文网址&#xff1a;Ubuntu之apt-get系列--apt-get安装软件的方法/教程_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Ubuntu使用apt-get安装软件的方法。 安装软件 先更新列表 sudo apt-get update 安装软件 sudo apt-get install <package name>[<version>]…

importlib的使用、9个视图子类、视图集、drf之路由、drf之请求响应回顾、GenericViewSet相关流程图

一 drf之请求响应回顾 # 1 drf请求-请求对象&#xff1a;data&#xff0c;query_params&#xff0c;其他跟之前一样&#xff0c;FILES-默认&#xff1a;支持三种编码-局部配置&#xff1a;视图类中-from rest_framework.parsers import JSONParser, FormParser, MultiPartPars…

MySQL的故事——MySQL架构与历史

MySQL架构与历史 文章目录 MySQL架构与历史一、MySQL逻辑架构二、并发控制三、事务四、多版本并发控制(MVCC) 一、MySQL逻辑架构 第一层&#xff1a;连接处理、授权认证、安全等等 第二层&#xff1a;查询解析、分析、优化、缓存以及所有的内置函数。包含跨存储引擎的功能&…

【数据结构与算法】十大经典排序算法

文章目录 前言一、常见十大排序算法总结1、名词解释2、时间复杂度 二、排序算法与C语言实现1、冒泡排序2、选择排序3、插入排序4、希尔排序5、归并排序6、快速排序7、堆排序8、计数排序9、桶排序10、基数排序 总结 前言 排序算法是《数据结构与算法》中最基本的算法之一。 排序…

java:操作cookie

背景 cookie 是一种客户端会话技术&#xff0c;将数据保存到客户端。主要流程就是&#xff1a; 1、服务器把数据设置到cookie并返回给浏览器 2、浏览器自动保存 3、浏览器下一次发送请求自动携带cookie给服务器 我们主要来看一下 java 是怎么操作 cookie 的。 cookie介绍 特…

区块链技术与应用 - 学习笔记2【密码学基础】

大家好&#xff0c;我是比特桃。本系列笔记只专注于探讨研究区块链技术原理&#xff0c;不做其他违反相关规定的讨论。 区块链技术已被纳入国家十四五规划&#xff0c;在“加快数字发展 建设数字中国”篇章中&#xff0c;区块链被列为“十四五”七大数字经济重点产业之一&#…

Node.js安装使用

目录 一、安装 Node.js二、环境变量配置三、npm常用命令 Node.js 是一个强大的运行时环境&#xff0c;它使您能够在服务器端运行 JavaScript 代码。它非常流行&#xff0c;用于构建 Web 应用程序、API 和各种后端服务。 一、安装 Node.js 1、访问 Node.js 官方网站。 在主页上…

算法训练day34|贪心算法 part03(LeetCode 1005.K次取反后最大化的数组和、134. 加油站、135. 分发糖果(处理一边再处理一边))

文章目录 1005.K次取反后最大化的数组和思路分析代码实现 134. 加油站暴力方法贪心方法 135. 分发糖果(处理一边再处理一边)思路分析代码实现思考总结 1005.K次取反后最大化的数组和 题目链接&#x1f525; 给定一个整数数组 A&#xff0c;我们只能用以下方法修改该数组&#…

使用redis实现队列功能

使用redis实现队列功能 操作方法描述LPUSHLong lPush(String key, String… values)将一个或多个值 value 插入到列表 key 的表头&#xff0c;返回插入后列表中value的数量&#xff0c;若key不存在&#xff0c;会创建一个新的列表并执行 LPUSH 操作RPOPLPUSHString rPopLPush(S…

hive安装步骤

centos7安装hive&#xff0c;hive版本3.1.2 一、环境准备 JDK版本&#xff1a;jdk-8u381 Hadoop版本&#xff1a;hadoop-3.1.3 MySQL版本&#xff1a;8.0.34 二、hive安装步骤 1.解压 将压缩包复制到/opt/software目录下&#xff0c;并解压至/opt/module/目录 tar -zxf…

bootstrap 导航栏下拉菜单,居右下拉菜单

<!DOCTYPE html> <html lang"en"> <head><!-- 新 Bootstrap5 核心 CSS 文件 --> <link rel"stylesheet" href"bootstrap-5.0.0-beta1-dist/css/bootstrap.min.css"><!-- 最新的 Bootstrap5 核心 JavaScript 文…

Ubuntu22.04 LTS 显卡相关命令

第一部分查看驱显卡信息 一、查看显卡型号 # -i表示不区分大小写 lspci | grep -i nvidia # 必须安装好nvidia驱动 nvidia-smi -L 二、查看显卡驱动版本 cat /proc/driver/nvidia/version 三、查看CUDA、cuDNN版本 # 或者 nvcc -V&#xff08;两个显示的版本一致&#xf…