JVM常见面试题
1.什么是JVM
- JVM(Java Virtual Machine,Java虚拟机)是用于运行 Java 字节码的虚拟机进程。它是 Java 实现跨平台能力的核心,能够将 Java 程序编译为与平台无关的字节码,并根据操作系统将其转换为可直接执行的机器码。JVM 负责管理应用程序的内存,包括内存分配和垃圾回收,确保程序的高效运行。
2.JVM的内存模型
- 方法区:存放已经被虚拟机加载的类信息、常量、静态变量等数据,是线程共享区域。
- 堆:存放对象的实例和数组,线程共享,是垃圾回收的主要区域。
- 虚拟机栈:每个线程都有自己的虚拟机栈,属于线程私有的内存区域。当线程执行一个方法时,会在栈中压入一个栈帧。每个栈帧包含局部变量表、操作数栈、动态链接和方法返回地址等信息。
- 本地方法栈:服务于本地方法调用,类似于java虚拟机栈,但服务的是Native方法。
- 程序计数器:跟踪当前线程的字节码指令,每个线程都有自己独立的程序计数器,用于保存下一条要执行指令的地址。
3.类的加载过程
- 加载:读取.class文件数据到内存,创建class对象
- 链接:
- 验证:确保类文件的字节码符合JVM规范,保证安全性。
- 准备:为静态变量分配内存,将其初始化为默认值。
- 解析:将常量池中的符号引用转换为直接引用。
- 初始化:对静态变量和静态代码块执行初始化。
4.有哪些类加载器?
- 类加载器(ClassLoader)是负责将字节码文件加载到JVM的模块。主要包括以下几类:
- 启动类加载器(Bootstrap ClassLoader),主要加载java核心类库(通常是jre目录下的lib目录),它是整个类加载系统的根加载器。位于其他所有类加载器之上。
- 扩展类加载器(Extension ClassLoader),主要加载java的扩展库(jre目录下lib目录下的ext中的文件),位于启动类加载器和应用程序类加载器之间。
- 应用程序类加载器(Application ClassLoader),也被称为系统类加载器,主要负责加载位于classpath路径下的所有类和jar文件,包括用户编写的类和依赖的第三方库。
- 自定义类加载器,通过继承ClassLoader来实现自定义加载逻辑。
5.什么是双亲委派
- 双亲委派是一种类加载机制,其中类加载器尝试加载类时,首先会委派给父类加载器尝试加载,直到最顶层的启动类加载器。如果父类加载器无法加载该类,子类才会尝试自行加载。这种模式保证了java核心库的类型安全,避免了类的重复加载。
6.堆和栈的区别
- 堆(Heap):动态分配内存,存储对象的实例。生命周期较长,由垃圾回收期管理,线程共享。
- 栈(Stack):存储局部变量和方法调用,生命周期较短,随方法的执行开始和结束。每个线程拥有自己的栈。
7.如何判断对象可以被回收
- 引用计数法:给对象添加一个引用计数器,每当一个地方引用它时,计数器加一,引用失效时计数器减一,当计数器值为0时,对象不再被使用,执行垃圾回收时,即可被回收。有个缺点就是循环引用问题,引用计数法无法处理对象之间的循环引用,例如A引用B,B引用A,这种情况下,引用计数器不会为0,导致内存泄漏。
- 可达性分析算法:从一系列的根对象(GC Roots)开始向下搜索,搜索所经过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,即证明该对象不再被使用,执行垃圾回收时,即可被回收。
8.你知道有哪些垃圾回收算法?
- 标记-清除:先标记出所有要回收的对象,然后统一清除这些对象。优点:实现简单。缺点:会产生大量的内存碎片,影响大对象分配。
- 复制:将内存划分为两部分,每次只使用其中一部分,执行垃圾回收时,将正在使用的内存中活动的对象复制另一块未被使用的内存中,然后清除正在使用的内存中所有的对象。优点:效率高,没有内存碎片,适合新生代。缺点:内存利用率低。
- 标记-整理:首先标记出所有的存活对象,然后将这些对象移动到内存的一端, 最后清除边界外的对象。优点:相比标记清除,标记整理解决了内存碎片问题。缺点:移动对象成本较高,效率低于复制算法。
- 分代回收算法:将堆内存分为新生代和老年代,新生代采用复制算法,老年代采用标记整理或标记清除。新生代包括一个伊甸园区(Eden)和两个幸存者区(Survivor From 和 To),新生代中的对象大部分都是朝生夕死,生命周期较短。新创建的对象一般都会被分配在新生代中的伊甸园区中,当伊甸园区内存不足时会执行Minor GC,通过可达性分析算法找到存活的对象,复制到幸存者To区,清除伊甸园区不再被引用的对象,幸存者To和幸存者From交换(就是From变成To,To变成From),同时对象的年龄加一,当伊甸园区内存再次不足时,会再次根据可达性分析算法找到存活的对象,将伊甸园区和幸存者From中存活的对象复制到幸存者To中,清除伊甸园区和幸存者From中的垃圾对象,再将幸存者To和幸存者From进行交换。同时对象年龄加一,当年龄超过一定阈值时(默认是15次),将被晋升到老年代。老年代中存放的就是在新生代中经过多次垃圾回收依然存活的对象(不过也有特殊情况,如果新生代中没有足够的空间来存放一个较大的对象,这些对象会被直接分配到老年代)。当老年代空间不足是时,会执行Full GC。
9.什么是永久代
- 永久代是JVM内存模型中的一部分,用于存储类的元数据,静态变量,常量池方法信息。在jdk8及以后,被元空间取代,原因:永久代固定大小,容易引发OOM,而元空间在本地内存中分配,动态调整大小,元空间将类的元数据存放在堆外内存,降低了GC压力。
10.什么是STW
- Stop The World(STW),指的是垃圾收集过程中,虚拟机暂停所有应用线程的执行,以确保在清理内存时不会有新的数据改动。保证垃圾收集的安全性和一致性,STW是收集过程中的一部分,影响程序的暂停时间。
11.JVM的主要组成部分
- 类加载器:负责加载类文件
- 运行时数据区:包括堆、方法区、程序计数器、虚拟机栈、本地方法栈。
- 执行引擎:执行类文件中的指令。
- 本地库接口:连接和管理本地库(Native Library),允许java调用本地引用程序和库。
- 垃圾收集器:管理和回收内存。
12.什么是内存泄漏?
- 内存泄漏是指程序中已被分配的内存由于某种原因未能释放,导致随着程序运行时间的增长,越来越多的内存无法被使用,导致可用内存逐渐减少,最终可能导致程序性能下降或崩溃的现象。
13.常用的JVM垃圾回收器有哪些?
- Serial GC (串行垃圾回收器)
- 特点:单线程工作,适用于单核或者低内存环境
- 适用场景:适合小型应用或不需要高并发的场景
- 使用方法:-XX:+UseSerialGC
- Parallel GC (并行垃圾回收器)
- 特点:多线程垃圾回收,注重吞吐量
- 适用场景:适合CPU资源较多、要求高吞吐量的场景
- 使用方法:-XX:+UseParallelGC (JDK8默认)
- CMS GC (Concurrent Mark-Sweep)
- 特点:采用标记-清除算法,可以再应用运行期间并发进行回收,减少停顿时间
- 适用场景:适合对响应有较高要求的应用
- 使用方法:-XX:+UseConcMarkSweepGC
- 缺点:在回收碎片时可能会产生Full GC ,并且会消耗较多CPU资源
- G1 GC (Garbage-First)
- 特点:分区分代管理内存,优先回收垃圾最多的区域,以达到高效回收和低停顿的效果
- 适用场景:适合大型内存应用,能均衡停顿时间和吞吐量
- 使用方法:-XX:+UseG1GC (默认在JDK9及以上版本)
14.常用的JVM调优参数有哪些?
- 堆设置:
- -Xms:设置堆的初始大小
- -Xmx:设置堆的最大大小
- -Xmn:设置新生代大小(对于Parallel GC)
- 垃圾收集器设置:
- -XX:+UseSerialGC
- -XX:+UseParallelGC
- -XX:+UseConcMarkSweepGC
- -XX:+UseG1GC
- 性能优化:
- -XX:SurvivorRatio:设置Eden区和Survivor区的大小比例
- -XX:NewRatio:设置老年代与新生代的堆空间比例
- -XX:MaxPermSize:设置永久代最大空间(java8之前)
- -XX:MetaspaceSize:设置元空间初始大小(java8及以后)
- -XX:UseStringDeduplication:开启G1垃圾收集器的字符串去重功能
- 调试和监控:
- -XX:+PrintGCDetails:打印垃圾回收的详细信息
- -XX:+PrintGCDateStamps:在垃圾收集日志中添加时间戳
- -Xloggc:<filename>:将垃圾回收日志写入文件
- -XX:+HeapDumpOnOutOfMemoryError :在内存溢出时生成堆快照文件
- -XX:HeapDumpPath=/path/to/directory/heapdump.hprof:将生成的堆快照文件保存到指定的路径和文件名中
- Minor GC 、Major GC、Full GC三者区别及触发条件?
- Minor GC:清理新生代的垃圾,触发条件:当新生代空间不足时触发,即新生成的对象填满新生代的Eden区时。
- Major GC:主要针对老年代的垃圾收集过程,速度比Minor GC 慢。触发条件:老年代空间不足时触发,或者在Minor GC 后存活对象转移到老年代填满可用空间时。
- Full GC:对整个堆以及方法区的垃圾回收,是最耗时的GC,会造成较长时间的停顿。触发条件:调用System.gc(),老年代空间不足时,方法区空间不足时,极少数情况下通过 JNI 分配大量堆外内存导致堆外内存不足时,也可能触发 Full GC。
16.JVM中都有哪些引用类型?
- 强引用(Strong Reference):普通的对象引用,只要强引用还存在,垃圾回收器就不会回收被引用的对象。Object obj = new Object();
- 软引用(Soft Reference):通过SoftRefReference类实现,在垃圾回收中,只有当内存不足时,才会尝试回收软引用指向的对象。适合用来实现内存敏感的缓存。SoftReference softRef = new SoftReference<>(new Object());
- 弱引用(Weak Reference):通过WeakReference类实现,比软引用更弱,JVM 每次进行垃圾回收时,都会回收弱引用指向的对象,无论内存是否紧张。适合用来存储那些偶尔需要但并非必须的对象。WeakReference weakRef = new WeakReference<>(new Object());
- 虚引用(Phantom Reference):通过 PhantomReference 类实现,最弱的一种引用关系,不会决定对象的生命周期,只是用来跟踪对象的回收状态,必须与引用队列(ReferenceQueue)联合使用。
- ReferenceQueue queue = new ReferenceQueue<>();
- PhantomReference phantomRef = new PhantomReference<>(new Object(), queue);