Mushroom Notes Mushroom Notes
🍄首页
  • JavaSE

    • 基础篇
    • 数据结构
    • IO流
    • Stream流
    • 函数式接口
    • JUC
    • 反射
    • 网络编程
    • 设计模式
  • JavaEE

    • Servlet
    • JDBC
    • 会话技术
    • 过滤器监听器
    • 三层架构
  • JDK

    • 总览
  • JVM

    • 总览
  • 常用mate
  • CSS
  • JavaScript
  • rds 数据库

    • MySQL
    • MySQL 进阶
    • MySQL 库表规范
  • nosql 数据库

    • Redis
    • Redis 进阶
    • Redis 底层
    • MongoDB
  • Spring生态

    • Spring
    • Spring MVC
    • Spring boot
    • Spring Validation
  • Spring Cloud生态

    • Spring Cloud
    • 服务治理
    • 远程调用
    • 网关路由
    • 服务保护
    • 分布式事务
    • 消息中间件
  • 数据库

    • Mybatis
    • Mybatis Plus
    • Elasticsearch
    • Redisson
  • 通信

    • Netty
📚技术
  • 方案专题
  • 算法专题
  • BUG专题
  • 安装专题
  • 网安专题
  • 面试专题
  • 常用网站
  • 后端常用
  • 前端常用
  • 分类
  • 标签
  • 归档

kinoko

一位兴趣使然的热心码农
🍄首页
  • JavaSE

    • 基础篇
    • 数据结构
    • IO流
    • Stream流
    • 函数式接口
    • JUC
    • 反射
    • 网络编程
    • 设计模式
  • JavaEE

    • Servlet
    • JDBC
    • 会话技术
    • 过滤器监听器
    • 三层架构
  • JDK

    • 总览
  • JVM

    • 总览
  • 常用mate
  • CSS
  • JavaScript
  • rds 数据库

    • MySQL
    • MySQL 进阶
    • MySQL 库表规范
  • nosql 数据库

    • Redis
    • Redis 进阶
    • Redis 底层
    • MongoDB
  • Spring生态

    • Spring
    • Spring MVC
    • Spring boot
    • Spring Validation
  • Spring Cloud生态

    • Spring Cloud
    • 服务治理
    • 远程调用
    • 网关路由
    • 服务保护
    • 分布式事务
    • 消息中间件
  • 数据库

    • Mybatis
    • Mybatis Plus
    • Elasticsearch
    • Redisson
  • 通信

    • Netty
📚技术
  • 方案专题
  • 算法专题
  • BUG专题
  • 安装专题
  • 网安专题
  • 面试专题
  • 常用网站
  • 后端常用
  • 前端常用
  • 分类
  • 标签
  • 归档
  • 方案专题

  • 算法专题

  • BUG专题

  • 安装专题

  • 网安专题

  • 面试专题

    • Java 基础篇
    • JUC篇
    • JVM篇
      • JDK、JRE、JVM 的区别
      • 说一下JVM的组成部分?及其作用?
      • 你知道JVM运行时数据区吗?
      • JVM哪些部分会出现内存溢出?
      • 说说方法区与永久代、元空间之间的关系?
      • 你知道JVM的内存参数吗?
      • 你可以描述一下GC的流程吗?
      • 怎么判断对象可以被回收?
      • 你知道JVM垃圾回收算法有哪些?
      • 说说GC和分代回收算法
      • 强弱软虚引用的理解
      • 你知道哪些垃圾回收器?
      • 你有遇到过内存溢出吗?怎么解决的?
      • 你知道有什么内存泄漏的情况吗?
      • 可以描述一下类加载过程吗?
      • 你知道双亲委派机制吗?
      • 说一下 你知道的JVM 性能监控的工具?
      • 谈一谈你对finalize方法的理解
    • SSM篇
    • Springboot篇
    • SpringCloud篇
    • MQ篇
    • MySQL篇
    • Redis篇
    • 设计模式篇
    • Elasticsearch篇
  • 专题
  • 面试专题
kinoko
2023-12-26
目录

JVM篇面试题

# JDK、JRE、JVM 的区别


image.png

  • JDK(Java Development Kit)是整个Java 的核心,是java开发工具包
    • 包括了 Java 运行环境 JRE、Java 工具和 Java 基础类库。
  • JRE(Java Runtime Environment)是运行 JAVA 程序所必须的环境的集合
    • 包含java虚拟机和java程序的一些核心类库。
  • JVM是 Java Virtual Machine(Java 虚拟机)的缩写,是整个 java 实现跨平台的最核心的部分
    • 能够运行以 Java 语言写作的软件程序。

面试话术
三者是一个包含关系,jdk是java开发工具包,包含JRE和JVM,JRE就是java运行环境,核心类库那些,JVM就是java虚拟机,是java能够跨平台运行的最核心的部分,所以在开发java之前都必须安装jdk。

# 说一下JVM的组成部分?及其作用?


面试话术
有类加载器、运行时数据区、执行引擎、本地库接口
类加载器加载class文件到运行时数据区,由于字节码文件只是JVM的一套指令规范,并不能直接交给底层操作系统去执行,所以需要执行引擎将字节码翻译成底层操作系统指令,再交给CPU去执行,而这个翻译过程就需要调用其他语言的本地库接口来实现,如C++。

# 你知道JVM运行时数据区吗?


image.png
面试话术
JVM的内存结构主要分为两部分,线程共享和线程私有,线程共享的有堆内存、方法区,线程独占的有程序计数器、本地方法栈、虚拟机栈。
方法区是jvm规范中的一个定义,定义虚拟机要划分出一块内存来作为方法区,用于存储类的信息、方法字节码、常量、静态变量、静态方法等数据,然后永久代是jdk1.8之前的实现,jdk1.8之后变为了元空间。
堆内存存放了几乎所有的类实例,也是垃圾回收的主要区域,jdk1.7常量是由方法区移到了堆内存,但是随着方法区的实现变为了元空间,常量池又回到了方法区。
虚拟机栈是方法内部模型,主要存放局部变量等线程独占的资源。
程序计数器就是记录当前程序或者方法执行到哪里了,在多线程的上下文切换时就会起到作用。

# JVM哪些部分会出现内存溢出?


面试话术
只有程序计数器不会oom吧,不过我其实没怎么遇到过oom,最多也就遇到stackoverflow栈溢出,就是写递归的时候递归次数太多了就栈溢出了,其他的好像对象建太多的话会堆内存溢出,线程开太多不释放也会栈溢出,哦,类加载太多也会方法区内存溢出。

# 说说方法区与永久代、元空间之间的关系?


面试话术
方法区其实只是规范中的一个定义,定义虚拟机要划分出一块内存来作为方法区,用来存储类的信息、方法字节码等数据,具体实现是交给不同系统的虚拟机自己实现,永久代是jdk1.8之前的实现,jdk1.8后方法区的实现就变成元空间了。

# 你知道JVM的内存参数吗?


如题:
image.png

-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
-Xmn500M 设置年轻代为500m;
-XX:PermSize=500M ;设置永久代最小内存  1.8之后采用 MetaspaceSize
-XX:MaxPermSize=500M ;设置永久代最大内存  1.8之后采用 MaxMetaspaceSize
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息。
-XX:MaxDirectMemorySize : 设置直接内存的大小
1
2
3
4
5
6
7
8
9
10
11
12
13

面试话术
常用的有
-Xms 设置堆最小内存
-Xmx 设置堆最大内存
-Xss 设置栈内存大小
-XX:NewRatio 设置新生代和老年代的比例
-XX:SurvivorRatio 设置伊甸园跟幸存者区的比例
-XX:MaxPermSize 设置永久代大小,1.8以后已经移除
-XX:MaxMetaspaceSize 设置元空间最大内存大小

# 你可以描述一下GC的流程吗?


面试话术
创建就是走类的生命周期嘛,加载、连接、初始化、使用、销毁,销毁就相当于是被垃圾回收了。一个对象被创建,一般就是会进到伊甸园,当伊甸园内存满了之后,就会进行GC,没有被回收掉的对象,则会进入S区,由于S区用的是标记复制法进行回收嘛,所以会在两个S区之间来回移动,也就是S区中的form和to区,经过15轮GC都没有被回收掉的对象就会晋升到老年代。因为老年代的内存分配一般会比较大,所以一般不会频繁的GC,当老年代需要进行GC的时候,就会需要STW,就会感到程序卡顿。有一些大对象,创建后由于伊甸园装不下,会直接到老年代。

# 怎么判断对象可以被回收?


面试话术
我记得是有两个算法来支持这个判断,引用计数器算法和可达性分析算法。
引用计数器就是对对象的引用进行计数,计数为0时则代表可回收,但是存在循环引用的问题,比如A对象里面引用了B对象,当设置A对象为null,B对象为null时GC没办法回收,因为AB对象的引用计数器都还未清0,需要进入对象内部进行设置null才能完全使引用计数器为0,较为繁琐,所以jvm没有使用这种算法。
jvm使用的是可达性分析算法,就是从GC Root开始向下检索,走过的路径成为引用链,引用链外的则证明是不可达的,也就是回收对象。GC Root的选取一般就是我们程序中的变量,比如栈里面的内容,入参,局部变量,以及方法区里的内容静态变量,常量等,这样就能避免循环引用的问题。

# 你知道JVM垃圾回收算法有哪些?


**标记清除法:**标记还在被使用的对象,清除没有被标记的对象

  • 原理:以局部变量、静态变量这些为根对象,根据根对象的引用链来添加标记
  • 缺点:会产生内存碎片,也就是很多不连续的内存,当需要连续内存时(比如数组)无法达到需求

image.png
**标记整理法:**在标记清除算法上优化,标记、清楚、整理内存

  • 原理:在标记清除法的基础上增加一个整理环节、避免内存碎片的出现
  • 缺点:效率低,常用于老年代

image.png
**标记复制法:**标记、清除、复制、清除

  • 原理:标记清除后将标记对象复制到空闲的区域,全部复制后直接清除旧区域
  • 特点:适合新生代不适合老年代

image.png
面试话术
我知道的有三种,标记清除法、标记整理法、标记复制法
标记清除法就是标记还在使用的对象,清除没有使用的对象,原理是根据引用链来标记,这种算法导致出现很多的内存碎片。
标记整理法就是在标记清除算法的基础上加了一个整理的环节,避免了内存碎片的出现,但是会影响效率,不建议在频繁垃圾回收的区域进行,所以常用于老年代。
标记复制法就是标记要保留的对象,复制到一块空闲的区域,然后清除掉原来区域的所有对象,适用于少部分对象复制,多对象复制还是会影响性能,所以适用于新生代不适用老年代。

# 说说GC和分代回收算法


面试话术
首先GC的目的在于实现无用对象内存的释放、减少堆内存的无效内存占用、加快内存分配速度,用三色标记法之类的标记存活对象,回收未标记对象。GC的具体实现由垃圾回收器进行,我知道的垃圾回收器有Parallel GC、CMS(ConcurrentMarkSweep) GC、G1 GC,呃,暂时想到这三个。然后GC大多都采用分代回收思想,不同区域应用不同的回收策略。
GC根据规模又分Minor GC、Mixed GC、Major GC和Full GC,Minor GC发生在新生代,回收暂停时间短,Mixed GC发生在新生代和老年代的部分区域,G1 回收器特有,Major GC是老年代的垃圾回收,回收暂停时间长,Full GC是新生代和老年代的完整垃圾回收,暂停时间非常长,应该尽可能的避免。
分代回收算法的大致流程是新生代中,当伊甸园内存不足时,触发GC,回收后的幸存对象就会到幸存区,幸存区内存不足时也会触发GC,采用标记复制算法,然后幸存区中熬过15次左右的回收的对象就会晋升到老年代。

# 强弱软虚引用的理解


面试话术
强引用就是指就算出现OOM都不会回收的对象,弱引用就是指系统内存充足时不会回收,但是当内存不足时就会被回收的对象,软引用就是指不管系统内存够不够,只要GC都回收的对象,虚引用,我对这个概念还比较模糊,他好像跟没有任何引用一样,任何时候都可能会被回收掉。

# 你知道哪些垃圾回收器?


面试话术
串行的垃圾回收器,Serial GC,单线程进行GC,新生代采用标记复制法,老年代采用标记整理法,特点是简单高效,拥有很高的单线程回收效率。
并行的垃圾回收器,Paraller GC,多线程并发执行GC,同样的新生代采用标记复制法、老年代采用标记整理法,特点是注重吞吐量。
并发的垃圾回收器,CMS垃圾回收器,current mark sweep GC,采用并发标记清除算法,主要针对老年代进行回收,大致流程是先STW进行初始标记,仅标记GC Root ,速度非常快,然后通过可达性算法并发标记存活对象,然后再STW重新标记,相当于最终检查,最后进行并发清除。这个就注重的响应时间,不过采用的是标记清除法,就会有内存碎片,而且有Fallback策略,当对象产生速度大于回收速度的时候会触发Full GC。
并发的垃圾回收器,G1 垃圾回收器,在jdk9以后的版本,程序的默认垃圾回收器已经设置成G1 GC了,G1相比CMS的优势在于清除阶段,G1 将堆内存划分的更细,每个区域都能充当伊甸园、幸存者区、老年代,同样是经过初始标记、并发标记、重新标记、在清除的时候,G1 垃圾回收器会通过回收时间和回收后能够释放的空间计算每个区域的回收价值,选择部分回收价值高的区域进行回收。特点就是逻辑上保留了分代回收的思想,整体上是采用标记整理法进行回收,不会产生内存碎片,而且可以配置规定垃圾回收时间,同样也有Fallback机制,G1 兼顾了吞吐量和响应时间。

# 你有遇到过内存溢出吗?怎么解决的?


面试话术
很少,但是我记得几种情况会导致内存溢出,比如误用线程池,特别是使用Executors的使用,默认创建的线程池上限太高,我记得是integer.MAX还是什么,就是最大线程数是整型的最大值,这样会导致线程数创建太多,如果没能即时释放的话,就会导致栈溢出。还有一次性查询数据量过大的时候也会,就是一下子创建太多对象了,所以要尽量避免对数据量大的表进行findAll的操作,还有的话可能是动态生成类过多吧,造成方法区内存溢出。

# 你知道有什么内存泄漏的情况吗?


面试话术
内存泄漏就是说,我一个对象已经没有在用了,但是垃圾回收器又回收不掉,这种情况被称为内存泄漏嘛,其实这里有一个我觉得是比较常见的一个场景,会导致这个问题。就是ThreadLocal这个类,我们一般会使用这个类底层的ThreadMap来存放东西嘛,获取token中的用户信息,在过滤器或者拦截器那拿到token然后解析载荷,将用户信息放到ThreadLocal里,以方便后续的业务进行获取用户信息嘛,然后这里其实是有一个使用的注意事项的,就是这个请求执行完成后,应该删掉该请求在ThreadLocal中的数据。因为这个ThreadLocal是按线程进行存储的嘛,每一个请求都是一个线程,而且我们要这样用的话得把ThreadLocal抽成一个静态对象来使用嘛,所以等于是共享了一个ThreadMap,那我请求结束了,存进去,不删,只会导致这个对象越来越大,最后就内存泄漏了,因为jvm没办法回收map里面的数据嘛,他认为这个map还在引用这些数据。

# 可以描述一下类加载过程吗?


面试话术
类加载主要分为三个阶段:加载、连接、初始化。
加载就是加载该类的.class字节码文件进方法区嘛。
然后连接分为三个部分:验证、准备、解析

  1. 验证就是保证类加载的准确性嘛,比如验证一下jdk版本是否与编译的一致,验证语法是否正确
  2. 准备就是为类中的静态变量或者常量分配内存空间,赋予初始值
  3. 解析就是将类中的符号引用转换为直接引用,就是从字符串变为有实际意义的关键字

初始化就是给静态变量以及常量赋值,并执行静态代码块,如果有继承父类则会先初始化父类

# 你知道双亲委派机制吗?


image.png
面试话术
java有四级类加载器嘛,最高级就是bootstrap类加载器,启动类加载器,加载jdk绝大部分的类。然后Extension类加载器,这个是扩展类加载器,再下来是application类加载器,系统类加载器,我们自己写的类就是由这个加载器加载的,最后一个就是自定义的类加载器,也就是我们自己写一个类继承ClassLoader,比如Tomcat就有根据j2ee规范自行实现类加载器。
双亲委派机制就是一个类加载器在接收到类加载请求的时候,首先他不会自己去尝试加载这个类,而是询问上级类加载器是否能够加载这个类,比如我们写了一个类,有main方法,在编译的时候就会触发这个类的类加载,然后application类加载器就会询问extension类加载器,然后extension类加载器也不会自己尝试加载,同样也会将请求上抛给上级,也就是bootstrap类加载器,当bootstrap发现这个类不是jdk原生的类,他加载不了,就会将请求传回给下级类加载器,extension类加载器发现自己也加载不了,最终请求回到application类加载器,然后由application类加载器进行加载,如果全部类都无法加载的话则会报错。

# 说一下 你知道的JVM 性能监控的工具?


面试话术
JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。
jconsole:用于对 JVM 中的内存、线程和类等进行监控;
jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等

# 谈一谈你对finalize方法的理解


面试话术
这个方法好像几乎没怎么写过,我只记得这是object类里的一个方法,重写后,这个类在被GC的时候就会调用这个方法,所以这个方法里一般是写些资源释放之类的代码。
不过后来好像是不建议这样做了,将资源释放之类的代码写在里面的话比较影响性能,严重时还会引起OOM,就比如在垃圾回收的时候介于在这个方法里进行了某些错误操作导致没能成功回收,导致越积越多之类的?所以在jdk9的时候就被标记为过时了。

#java#jvm#面试#虚拟机
上次更新: 2023/12/29 11:32:56
JUC篇
SSM篇

← JUC篇 SSM篇→

最近更新
01
JVM 底层
09-13
02
JVM 理论
09-13
03
JVM 应用
09-13
更多文章>
Theme by Vdoing | Copyright © 2022-2024 kinoko | MIT License | 粤ICP备2024165634号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式