Java垃圾回收机制你还不明白?一线大厂面试必问的!

简介: Java垃圾回收机制解析

什么是自动垃圾回收?

自动垃圾回收是一种在堆内存中找出哪些对象在被使用,还有哪些对象没被使用,并且将后者删掉的机制。
所谓使用中的对象(已引用对象),指的是程序中有指针指向的对象;而未使用中的对象(未引用对象),则没有被任何指针给指向,因此占用的内存也可以被回收掉。
在用 C 之类的编程语言时,程序员需要自己手动分配和释放内存。而 Java 不一样,它有垃圾回收器,释放内存由回收器负责。本文接下来将介绍垃圾回收机制的基本过程。
image

标记

垃圾回收的第一步是标记。垃圾回收器此时会找出哪些内存在使用中,还有哪些不是。
image

上图中,蓝色表示已引用对象,橙色表示未引用对象。垃圾回收器要检查完所有的对象,才能知道哪些有被引用,哪些没。如果系统里所有的对象都要检查,那这一步可能会相当耗时间。

清除

这一步会删掉标记出的未引用对象。
image

内存分配器会保留指向可用内存的引用,以供分配新对象。

压缩

为了提升性能,删除了未引用对象后,还可以将剩下的已引用对象放在一起(压缩),这样就能更简单快捷地分配新对象了。
image

为什么需要分代垃圾收集?

之前说过,逐一标记和压缩 Java 虚拟机里的所有对象非常低效:分配的对象越多,垃圾回收需时就越久。不过,根据统计,大部分的对象,其实用没多久就不用了。
来看个例子吧。(下图中,竖轴代表已分配的字节,而横轴代表程序运行时间)
image

上图可见,存活(没被释放)的对象随运行时间越来越少。而图中左侧的那些峰值,也表明了大部分对象其实都挺短命的。
欢迎大家关注我的公种浩【程序员追风】,文章都会在里面更新,整理的资料也会放在里面。

JVM 分代

根据之前的规律,就可以用来提升 JVM 的效率了。方法是,把堆分成几个部分(就是所谓的分代),分别是新生代、老年代,以及永生代。
image

新对象会被分配在新生代内存。一旦新生代内存满了,就会开始对死掉的对象,进行所谓的小型垃圾回收过程。一片新生代内存里,死掉的越多,回收过程就越快;至于那些还活着的对象,此时就会老化,并最终老到进入老年代内存。
Stop the World 事件 —— 小型垃圾回收属于一种叫 "Stop the World" 的事件。在这种事件发生时,所有的程序线程都要暂停,直到事件完成(比如这里就是完成了所有回收工作)为止。
老年代用来保存长时间存活的对象。通常,设置一个阈值,当达到该年龄时,年轻代对象会被移动到老年代。最终老年代也会被回收。这个事件成为 Major GC。
Major GC 也会触发STW(Stop the World)。通常,Major GC会慢很多,因为它涉及到所有存活对象。所以,对于响应性的应用程序,应该尽量避免Major GC。还要注意,Major GC的STW的时长受年老代垃圾回收器类型的影响。
永久代包含JVM用于描述应用程序中类和方法的元数据。永久代是由JVM在运行时根据应用程序使用的类来填充的。此外,Java SE类库和方法也存储在这里。
如果JVM发现某些类不再需要,并且其他类可能需要空间,则这些类可能会被回收。

世代垃圾收集过程

现在你已经理解了为什么堆被分成不同的代,现在是时候看看这些空间是如何相互作用的。后面的图片将介绍JVM中的对象分配和老化过程。
首先,将任何新对象分配给 eden 空间。两个 survivor 空间都是空的。
image

当 eden 空间填满时,会触发轻微的垃圾收集。
image

引用的对象被移动到第一个 survivor 空间。清除 eden 空间时,将删除未引用的对象。
image

在下一次Minor GC中,Eden区也会做同样的操作。删除未被引用的对象,并将被引用的对象移动到Survivor区。然而,这里,他们被移动到了第二个Survivor区(S1)。
此外,第一个Survivor区(S0)中,在上一次Minor GC幸存的对象,会增加年龄,并被移动到S1中。待所有幸存对象都被移动到S1后,S0和Eden区都会被清空。注意,Survivor区中有了不同年龄的对象。
image

在下一次Minor GC中,会重复同样的操作。不过,这一次Survivor区会交换。被引用的对象移动到S0,。幸存的对象增加年龄。Eden区和S1被清空。
image

此幻灯片演示了 promotion。在较小的GC之后,当老化的物体达到一定的年龄阈值(在该示例中为8)时,它们从年轻一代晋升到老一代。
image

随着较小的GC持续发生,物体将继续被推广到老一代空间。
image

所以这几乎涵盖了年轻一代的整个过程。最终,将主要对老一代进行GC,清理并最终压缩该空间。
image

最后

欢迎大家一起交流,喜欢文章记得关注我点个赞哟,感谢支持!

相关文章
|
4天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
16 2
|
9天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
14天前
|
存储 缓存 Oracle
Java I/O流面试之道
NIO的出现在于提高IO的速度,它相比传统的输入/输出流速度更快。NIO通过管道Channel和缓冲器Buffer来处理数据,可以把管道当成一个矿藏,缓冲器就是矿藏里的卡车。程序通过管道里的缓冲器进行数据交互,而不直接处理数据。程序要么从缓冲器获取数据,要么输入数据到缓冲器。
Java I/O流面试之道
|
9天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
31 6
|
11天前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
35 4
|
11天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
51 4
|
22天前
|
监控 算法 Java
深入理解Java的垃圾回收机制
【10月更文挑战第22天】在Java的世界里,有一个默默无闻却至关重要的角色——垃圾回收(Garbage Collection, GC)。就像城市的清洁工一样,它默默地清理着不再使用的内存空间,确保我们的程序运行得既高效又稳定。但你真的了解垃圾回收是如何工作的吗?让我们一起探索这个看似简单却充满奥秘的过程,看看它是如何影响你的Java应用性能的。
|
24天前
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
51 5
|
23天前
|
存储 Java
[Java]面试官:你对异常处理了解多少,例如,finally中可以有return吗?
本文介绍了Java中`try...catch...finally`语句的使用细节及返回值问题,并探讨了JDK1.7引入的`try...with...resources`新特性,强调了异常处理机制及资源自动关闭的优势。
18 1
|
1月前
|
Java 程序员
Java 面试高频考点:static 和 final 深度剖析
本文介绍了 Java 中的 `static` 和 `final` 关键字。`static` 修饰的属性和方法属于类而非对象,所有实例共享;`final` 用于变量、方法和类,确保其不可修改或继承。两者结合可用于定义常量。文章通过具体示例详细解析了它们的用法和应用场景。
28 3