Java面试题——第九篇(JVM)

1. Java中的强引用、软引用、弱引用和虚引用分别是什么

  1. 强引用
  • 最常见的引用类型,在Java中,默认情况下,任何普通的对象引用都是强引用
  • 只要一个对象有强引用指向他,垃圾回收器永远不会回收该对象,即使系统内存紧张。
  1. 软引用
  • 当系统内存不足时,垃圾回收器会回收软引用指向的对象,避免内存溢出。在内存充足时,这些对象不会被回收。
  • 软引用通常用于实现缓存机制,允许程序在不影响性能的前提下利用多余内存。
  1. 弱引用
  • 只要垃圾回收器发现只有弱引用指向某个对象,该对象会立即被回收,无论系统内存是否充足
  • 弱引用常用于防止内存泄漏,典型应用场景WeakHashMap。
  1. 虚引用
  • 虚引用的作用是跟踪对象的垃圾回收状态。在对象被回收时,虚引用会被放入一个ReferenceQueue,我们可以通过这个队列来执行一些清理或者其他后续操作。

2. Java中常用的垃圾收集器有哪些

  1. 新生代垃圾收集器
  • Serial收集器
    • 单线程收集器,适合小型应用和单处理器环境
    • 触发STW操作,所有应用线程在GC时暂停
    • 使用场景:适用于单线程应用
  • ParNew收集器
    • 是Serial收集器的多线程版本,能够并行进行垃圾收集。
    • 与CMS收集器配合使用时,通常会选择ParNew收集器作为新生代收集器。
    • 使用场景:适用于多处理器环境,通常配合CMS收集器使用
  • Parallel Scavenge收集器(吞吐量优先)
    • 也称为"吞吐量收集器",追求最大化CPU时间的利用率
    • 并行处理新生代垃圾回收,适合大规模后台任务处理,注重吞吐量而非延迟
  1. 老年代垃圾收集器
  • Serial Old收集器
  • Serial收集器的老年代版本,使用标记-整理算法进行垃圾回收。
  • Parallel Old收集器
  • Parallel Scavenge收集器的老年代版本,使用多线程并行标记-整理算法
  • CMS收集器
  • 并发标记-清除收集器,追求低延迟,减少GC停顿时间
  • 使用并发标记和清除算法,适合对响应时间有较高要求的应用
  • 缺点:可能产生内存碎片,并且在并发阶段可能会发生Concurrent Mode Failure,导致full gc。
  • G1收集器
  • 设计用于取代CMS的低延迟垃圾收集器,能够提供可预测的停顿时间
  • 通过分区来管理内存,并在垃圾收集时优先处理最有价值的区域,避免了CMS的内存碎片问题

3. Java中如何判断对象是否是垃圾?不同垃圾回收方法有何区别

  1. 引用计数法
  • 每个对象维护一个引用计数器,引用计数增加时,计数器加1。减少时,计数器减1,当引用计数器为0时,说明该对象不在被引用,可以被回收。
  • 优点:实现简单,实时性好
  • 缺点:无法处理循环引用的问题,两个对象互相引用时,引用计数器永远不会为0.
  1. 可达性分析算法
  • Java中垃圾回收主要 采用可达性分析算法,通过从一组称为"GC Roots"的对象出发,遍历所有可达的对象,凡是无法通过GC Roots到达的对象,均被视为垃圾。
  • 优点:能够解决循环引用问题
  • 缺点:需要消耗一定的资源进行标记。

GC Roots的来源

  • 线程栈中的引用:每个线程栈中的局部变量、参数等。
  • 类的静态变量:被类加载器加载后的类会存储在方法区,类的静态变量可以作为GC Roots。
  • JNI全局引用:通过JNI创建的全局引用可以作为GC Roots。

4. 为什么Java的垃圾收集器将堆分为老年代和新生代

主要是为了提高垃圾回收效率,依据对象的生命周期特点来进行优化。

对象生命周期特点:

  • 大多数对象存活时间短:大部分对象会很快变成垃圾,不再被使用,这些短生命周期的对象会分配在新生代。
  • 少部分对象存活时间长:一些长期存活的对象不会很快被回收,分配在新生代的对象经过多次垃圾回收仍存活的,将晋升到老年代。

所以按照存活时间分区管理更加高效,因为不同分区的生命周期不同,所以可以采用不同的清除算法来优化处理。

不同的回收算法

  • 新生代的回收:新生代采用 复制算法。因为新生代中大部分对象生命周期短,大部分会在一次GC中被回收,复制算法只需要在内存中保留少量存活对象,并将它们复制到Survivor空间,回收剩余区域。这种算法效率很高,适合新生代对象频繁创建和回收的特点。
  • 老年代的回收:老年代中对象存活时间长,回收效率低。使用 标记-整理算法 或者 标记-清除算法,更加适合老年代对象的特性。

分区后,可以减少GC暂停的时间。总而言之,分区是为了更高效的管理不同生命周期的对象

1.堆的分代机制

Java堆内存根据生命周期被划分为三部分

  • 新生代:存放新创建的对象
  • 老年代:存放存活时间较长的对象,通常是从新生代晋升过来的对象。
  • 永久代:(JDK8以前为永久代,JDK8以后为元空间),存放类的元数据信息,包括类的静态变量、方法等。

2.新生代结构

新生代进一步划分为三个区域

  • Eden区:所有新创建的对象首先分配到Eden区。
  • Survivor区:Eden区中存活的对象会被复制到Survivor区(一般分为两个区域S0,S1),经过多次GC存活的对象会逐渐晋升到老年代。
    新生代中采用 复制算法,每次垃圾回收时,将Eden和Survivor存活对象复制到另一个Survivor空间,效率高且避免内存碎片。

3. 老年代的作用

老年代用于存放生命周期较长的对象,通常是从新生代晋升而来的。老年代使用的回收算法不同于新生代,常用 标记-清除算法 或者 标记-整理算法 ,适合回收长生命周期的对象。

5. 为什么Java8移除了永久代并引入了元空间

Java8移除了永久代并引入元空间,主要是为了解决PermGen固定大小、容易导致内存溢出GC效率低的问题。元空间使用本地内存,具备更灵活的内存分配能力,提升了垃圾收集和内存管理的效率。

6. 为什么Java新生代被划分为S0、S1和Eden区

主要是为了提高新生代内存利用率。

因为新生代对象朝生夕死的特性,适合复制算法,按照正常思路将新生代一分为二,划两块区域。每次只使用其中一个,GC后将存活的复制到另一个区域,然后清理老区域非存活对象,这样替换使用两块区域可以避免内存碎片的存在。

但如果一分为二的话,空间利用率只有一半,浪费空间。基于这点,定义了三个区域,Eden区和两个Survivor区,Eden区+1个Survivor可以比二分之一大,提升利用率。默认Eden占80%,一个Survivor占10%。

如果单个Survivor放不下GC存活的对象怎么办
老年代兜底

也就是说Survivor放不下存活的对象,那么超出的对象直接晋升到老年代。如果老年代仍然放不下,则会触发GC。

7. 什么是三色标记算法

三色标记算法是现代垃圾回收器中常用的一种增量标记算法。可以与应用线程并发执行,用于标记哪些对象需要被回收,哪些需要被保留,减少一次性停顿带来的性能影响。非常适合 低延迟和实时垃圾回收的场景。

他通过将对象分为三种颜色来进行标记和追踪。

三色标记基本概念

  • 白色对象:表示还没有被垃圾回收器访问到的对象,这些对象有可能是垃圾。
  • 灰色对象:表示已经被访问到,但其引用的其他对象还没有被处理完。
  • 黑色对象:表示已经被访问到且其引用的所有对象也都已经标记完毕,这些对象不会被回收。

标记过程

  • 初始状态:所有对象都是白色。
  • 标记阶段:从根对象(GC Roots)开始,把根对象变为灰色,然后递归扫描所有灰色对象,将其引用的对象变为灰色。标记为"已访问",当灰色对象的所有引用都处理完毕时,灰色对象会变成黑色。
  • 最终阶段:经过扫描,所有存活的对象最中都会变为黑色,未被访问到的白色对象即为垃圾,会被清除。

8. Java中的young GC、old GC、full GC、mixed GC的区别是什么

  1. Young GC(Minor GC)
  • 作用范围:仅针对新生代(Eden和S0/S1)
  • 触发条件:当新生代内存被填满时触发
  • 执行方式:只回收新生代中的对象,老年代不受影响
  • 特点:回收频率高,回收时间短,因为新生代中的对象的大多数是短命对象,容易被回收。
  1. old GC (Major GC)
  • 作用范围:仅针对老年代
  • 触发条件:当老年代空间不足时触发,通常当新生代晋升到老年代的对象过多,或者老年代存活的对象数量达到一定阈值时。
  • 执行方式:只回收老年代的对象,新生代不受影响。
  • 特点:执行时间比young gc长,因为老年代中的对象存活时间更长,且数量更多。
  1. full GC
  • 作用范围:对整个堆内存进行回收
  • 触发条件:当老年代空间不足且无法通过Old GC释放足够空间,或者系统调用如System.gc()。
  • 执行方式:回收新生代、老年代中的对象,并且可能会伴随着元空间的回收。
  • 特点:回收之间最长,会触发整个JVM停顿(STW),对性能有较大影响,通常不希望频繁发生。
  1. mixed GC (仅适用于G1 GC)
  • 作用范围:同时回收新生代和部分老年代区域
  • 触发条件:当G1垃圾回收器发现老年代区域的垃圾过多时触发。
  • 执行方式:混合回收新生代和部分老年代区域,主要目的是减少老年代中的垃圾积压。
  • 特点:结合了YGC的快速回收和OGC的深度回收,尽量减少停顿时间,适用于大内存应用。

9. 什么条件下触发Java的young GC

  1. Eden空间不足
  • 新生代被划分为三个区域:Eden区、S0、S1,大部分新创建的对象会分配到Eden区。
  • 当Eden区的对象填满,无法再为新的对象分配空间时,young GC会被触发,回收新生代中不在使用的对象。

2.Eden区+Survivor区都装满

  • 如果Eden区和Survivor区空间都不足以存放新分配的对象时,触发。清理空间,并将幸存的对象转移到Survivor区或者老年代。
  1. 部分垃圾回收器在full gc前
  • 如Parallel Scavenge收集器的回收是在full gc前执行young gc。

10. 什么条件下触发full gc

  1. 老年代空间不足
  2. 永久代或者元空间(元空间设置了阈值)不足
  3. 调用system.gc()或者jmap -dump。
  4. 空间分配担保:当新生代的to区放不下从Eden区和from区拷贝过来的对象或者新生代对象晋升到老年代时,如果老年代没有足够的空间来容纳这些对象,会触发full gc。
  5. 新生代到老年代的晋升失败:年轻代中的大对象或长期存活的对象会晋升到老年代。如果此时老年代空间不足,也会引发full gc。
  6. 年轻代平均晋升大小计算:在要进行young gc的时候,根据之前统计数据发现平均晋升大小比现在老年代剩余空间大,就会触发full gc。

如何减少full gc的触发

  1. 调整堆内存大小:通过调整堆内存大小(-Xms和-Xmx)来减少老年代空间不足的情况。
  2. 增大新生代大小:增加新生代的大小,减少对象晋升到老年代的频率。
  3. 优化对象分配和生命周期:通过分析对象的生命周期,减少长时间存在的大对象,优化应用的内存使用模式。
  4. 合理设置元空间大小:避免元空间过小导致频繁的full gc。可以使用 -XX:MetaspaceSize和-XX:MaxMetaspaceSize来设置。
相关推荐
喜欢打篮球的普通人4 分钟前
rust模式和匹配
java·算法·rust
java小吕布17 分钟前
Java中的排序算法:探索与比较
java·后端·算法·排序算法
慢生活的人。23 分钟前
SpringSecurity+jwt+captcha登录认证授权总结
java·认证·rbac·权限·验证
Neophyte06081 小时前
C++算法练习-day40——617.合并二叉树
开发语言·c++·算法
向阳12181 小时前
LeetCode40:组合总和II
java·算法·leetcode
慕容复之巅1 小时前
基于MATLAB的条形码的识别图像处理报告
开发语言·图像处理·matlab
云空1 小时前
《InsCode AI IDE:编程新时代的引领者》
java·javascript·c++·ide·人工智能·python·php
zqzgng1 小时前
Python 数据可视化pilot
开发语言·python·信息可视化
写bug的小屁孩1 小时前
websocket初始化
服务器·开发语言·网络·c++·websocket·网络协议·qt creator
慧都小妮子1 小时前
Spire.PDF for .NET【页面设置】演示:复制 PDF 文档中的页面
java·pdf·.net