2024java高频面试之JVM

说说 JVM 内存区域


程序计数器

是「程序控制流的指示器,循环,跳转,异常处理,线程的恢复等工作都需要依赖程序计数器去完成」。程序计数器是「线程私有」的,它的「生命周期是和线程保持一致」的,我们知道,N 个核心数的 CPU 在同一时刻,最多有 N个线程同时运行,在我们真实的使用过程中可能会创建很多线程,JVM 的多线程其实是通过线程轮流切换,分配处理器执行时间来实现的。既然涉及的线程切换,所以每条线程必须有一个独立的程序计数器。

虚拟机栈

其描述的就是线程内存模型,「也可以称作线程栈」,也是每个「线程私有」的,「生命周期与线程保持一致」。在每个方法执行的时候,jvm 都会同步创建一个栈帧去存储局部变量表,操作数栈,动态连接,方法出口等信息。一个方法的生命周期就贯彻了一个栈帧从入栈到出栈的全部过程。

本地方法栈

概念很好理解,我们知道,java底层用了很多c的代码去实现,而其调用c端的方法上都会有native,代表本地方法服务,而本地方法栈就是为其服务的。

可以说是jvm中最大的一块儿内存区域了,它是所有线程共享的,不管你是初学者还是资深开发,多少都会听说过堆,毕竟几乎所有的对象都会在堆中分配。

方法区

也是所有「线程共享」的区域,它「存储」了被 jvm 加载的「类型信息、常量、静态变量等数据」。运行时常量池就是方法区的一部分,编译期生成的各种字面量与符号引用就存储在其中。

垃圾对象是怎么找到的?

1.引用计数算法

就是给对象添加一个计数器每当有一个地方引用它的时候,计数器就加1每当有一个引用失效的时候,计数器就减1「「当计数器的值为0的时候,那么该对象就是垃圾了」」这种方案的原理很简单,而且判定的效率也非常高,但是却可能会有其他的额外情况需要考虑。相互引用比如两个「「对象循环引用」」,a 对象引用了 b 对象,b 对象也引用了 a 对象,a、b 对象却没有再被其他对象所引用了,其实正常来说这两个对象已经是垃圾了,因为没有其他对象在使用了,但是计数器内的数值却不是 0,所以引用计数算法就无法回收它们。这种算法是比较「「直接的找到垃圾」」,然后去回收,也被称为"直接垃圾收集"。

2.根可达算法

这也是「「JVM 默认使用」」的寻找垃圾算法它的原理就是定义了一系列的根,我们把它称为 「「"GC Roots"」」 ,从 「「"GC Roots"」」 开始往下进行搜索,走过的路径我们把它称为 「「"引用链"」」 ,当一个对象到 「「"GC Roots"」」 之间没有任何引用链相连时,那么这个对象就可以被当做垃圾回收了。

GC Roots 有哪些?

在java中,有「「固定的GC Roots 对象」」和「「不固定的临时GC Roots对象」:

固定的GC Roots

1.在「「虚拟机栈(栈帧的本地变量表)中所引用的对象」」,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。在方法区中「类静态属性引用的对象」,譬如 Java 类的「引用静态变量」。在方法区中「「常量引用的对象」」,譬如字符串常量池中的引用。在方法区栈中 「「JNI (譬如 Native 方法)引用的对象」」。Java 「「虚拟机内部的引用」」,如基本数据类型对应的 Class 对象,一些常驻的异常对象(空指针异常、OOM等),还有类加载器。所有「「被 Synchronized 持有的对象」」。反应 Java 虚拟机内部情况的 「「JMXBean、JVMTI 中注册的回调本地代码缓存等」」。

临时GC Roots:

为什么会有临时的 GC Roots ?:目前的垃圾回收大部分都是「「分代收集和局部回收」」,如果只针对某一部分区域进行局部回收,那么就必须要考虑的「「当前区域的对象有可能正被其他区域的对象所引用」」,这时候就要将这部分关联的对象也添加到 GC Roots 中去来确保根可达算法的准确性。这种算法是利用了「「逆向思维」」,找到使用的对象,剩下的就是垃圾,也被称为"间接垃圾收集"。

分代收集理论

Generational Collection(分代收集)算法:主流JVM的垃圾回收算法,把堆分为新生代、老年代、永久带。

新生代:刚初始化的对象,状态不稳定,每次垃圾回收时都有大量对象被回收。大部分垃圾收集器对于新生代都采取Copying算法,因为复制对象的次数较少。一般来说是将新生代划分为一块较大的Eden(伊甸园)空间和两块较小的Survivor(幸存者)空间(一般为8:1:1),每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。

老年代:对象比较稳定,每次垃圾收集时只有少量对象需要被回收,一般使用的是Mark-Compact算法。

永久带:程序运行期间都不会被回收,一般位于方法区的静态区和常量池。

更多最新最全java面试学习资料(持续更新中。。。):
https://pan.quark.cn/s/236eda5a3dc2

相关推荐
半夏之沫40 分钟前
✨最新金九银十✨大厂后端面经✨
java·后端·面试
旋转的油纸伞2 小时前
大模型,多模态大模型面试【LoRA,分类,动静态数据类型,DDPM,ControlNet,IP-Adapter, Stable Diffusion】
算法·leetcode·面试·职场和发展·散列表
程序员yt3 小时前
2025秋招八股文--服务器篇
linux·运维·服务器·c++·后端·面试
web_code3 小时前
vite依赖预构建(源码分析)
前端·面试·vite
Algorithm15764 小时前
JVM是什么,与Java的关系是什么,以及JVM怎么实现的跨平台性
java·开发语言·jvm
王佑辉4 小时前
【jvm】所有的线程都共享堆吗
jvm
秋恬意5 小时前
LinkedList 源码分析
java·开发语言·面试
MJH8277 小时前
Selenium自动化测试中如何处理数据驱动?
自动化测试·软件测试·功能测试·selenium·测试工具·职场和发展·单元测试
JSON_L7 小时前
面试题整理1
后端·面试·php
鱼跃鹰飞8 小时前
大厂面试真题-简单描述一下SpringBoot的启动过程
java·spring boot·后端·spring·面试