【面试题-4】JVM

(1)类加载器有哪些?

启动类加载器

扩展类加载器

应用程序类加载器

自定义类加载器


(2)什么是双亲委派机制?

双亲委派机制,指一个加载器收到了加载类的请求时,不会直接加载这个类,

而是会向上委派给父类去加载,每一层都是如此,只有当父类返回无法加载时,子加载器才会尝试加载这个类。

这样设计的目的有两点:

(1)安全考虑,保证 Java 基础类在不同的环境还是同一个 class 对象,避免出现自定义类覆盖基础类的情况;

(2)避免类的重复加载;


(3)说下 JVM 的内存区域?

包括:

  • 类加载器;

  • 执行引擎;

  • 本地库接口;

  • 运行时数据区(方法区、堆、本地方法栈、虚拟机栈、程序计数器,前两个线程共用)

  • 方法区:存放被虚拟机加载的类信息,常量,静态变量等;

  • 堆:虚拟机中最大的一块,几乎所有的对象实例都在这里分配内存;

  • 本地方法栈:为虚拟机调用 native 方法服务;

  • 虚拟机栈:存储局部变量表、操作数栈、动态链接、方法出口等信息;

  • 程序计数器:记录每个线程的执行情况;

其中,堆又分为年轻代、老年代,年轻代又分 eden 区、from 区和 to 区。


(4)说下一下 Java 文件加载的流程呢?

编码的 Java 文件,是以 .java 结尾的,通过 javac 命令,由编译器把 Java 文件转为 .class 文件,

接着,JVM 的类加载器,把字节码文件加载到 JVM 的运行时数据区中,

但此时代码的指令还不能直接交给操作系统执行,

还需要执行引擎将代码指令翻译为底层操作的系统指令,再交给 CPU 执行,

这个过程需要调用其他语言的本地库接口实现。

参看博客:启动项目报JVM初始化错误


(5)说一下 JDK1.6、1.7、1.8 内存区域的变化?

主要是方法区的变化,在 JDK1.6 时,常量池统一放在方法区(永久代)中,

JDK1.7 时,字符串常量池从方法区移到了堆中,运行时常量池和类常量池还是在方法区中;

JDK1.8 时,取消了方法区,设置了元空间,并把其从 JVM 中移除,放到了直接内存里。


(6)能手写一个内存溢出的例子吗?

当然可以,如下:

java 复制代码
public class HeapOOM {
​	static class OOMObject{}
​	public static void main(String[] args) {
​		ArrayList <OOMObject> arraylist = new ArrayList();
​		while(true){
​			list.add(new OOMObject);
​		}
​	}
}

(7)内存泄漏可能由哪些原因导致的呢?

内存泄漏是指,JVM 中存在不再使用但无法被 GC 回收的对象,造成了堆内存的无效占用。

可能的原因:

  • ThreadLocal 没有 remove;

  • IO 没有释放;


(8)如何判断对象仍然存活?

(1)引用计数法:统计对象被引用的数量,少一个引用 - 1,当引用为 0 时,判断对象可被回收;

(2)可达性分析:由 GC Roots 对象出发,进行路径可达分析,凡可达的对象判断为存活;


(9)Java 中可作为 GC Roots 的对象有哪几种?

方法区中,静态属性引用的对象、常量引用的对象;

本地方法栈,JNI(Java Native Interface)引用的对象;

虚拟机栈,栈帧中局部变量表中引用的对象;


(10)Java 堆的内存分区了解吗?

堆中分为年轻代、老年代,内存比例默认是 1 :2,

其中年轻代中又分为 eden 区、from 区、to 区,内存比例为 8 :1 :1;


(11)垃圾收集算法了解吗?

垃圾收集算法有以下三种:

标记-清除:标记垃圾对象,进行回收(魔法棒,点到谁谁死掉),会造成内存不连续,碎片化;

标记-复制;内存一分为二,左右倒腾,进行回收,会造成资源浪费;

标记-整理:标记垃圾对象,回收同时将未被回收的对象对齐;


(12)Minor GC/Young GC 什么时候触发?

(1)新创建的对象存不下的时候;

(2)FullGC 触发的时候;


(13)什么时候会触发 Full GC?

(1)Young GC 前,老年代中可用的连续空间小于历代年轻代GC时进入到老年代对象的平均值;

(2)Young GC 后,进入到老年代的新生对象,老年代存不下;

(3)老年代空间不足,使用率高,达到一定比例时;

(4)年轻代的 To 区放不下来自 Eden 和 From 拷贝过来的对象时,老年代也放不下;

(5)System.gc() 命令;


(14)对象什么时候会进入老年代?

(1)熬过了15轮(默认,可设置)Young GC;

(2)对象大小过大;

(3)年轻代 To 区放不下 Eden 和 From 拷贝过来的对象;

(4)当 Young GC 中相同年龄的对象占了内存的一半或以上,大于这个年龄的对象会进入老年代;


(15)知道有哪些垃圾收集器吗?

分三类:

作用在年轻代:Serial、ParNew、Prallel Scavenge;

作用在老年代:CMS、Serial Old、Prallel Old;

作用在这两个区:G1;

一般来说,

年轻代垃圾收集器的收集算法为标记-复制,

老年代垃圾收集器的收集算法为标记-清除、标记-整理;


(16)详细说下 CMS 收集器的垃圾收集过程?

初始标记:单线程运行,需要 Stop The World,标记GC Roots能直达的对象。

并发标记:无停顿,和用户线程同时运行,从 GC Roots 直达对象开始遍历整个对象图。

重新标记:多线程运行,需要 Stop The World,标记并发标记阶段产生对象。

并发清除:无停顿,和用户线程同时运行,清理掉标记阶段标记的死亡对象。


(17)G1 收集器的垃圾收集过程了解吗?

初始标记:标记从 GC Root 开始直接关联可达的对象,Stop The World 执行。

并发标记:和用户线程并发执行,从 GC Root 开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象。

最终标记:Stop The World,标记在并发标记过程中产生的垃圾。

筛选回收:指定回收计划,选择多个 Region 构成回收集,把回收集中 Region 的存活对象复制到空的 Region 中,再清理掉整个旧 Region 的全部空间。需要 Stop The World。


G1 垃圾收集器的优点是可以手动设置 GC 时间,默认 200 ms


(18)如果CPU占用率100%怎么办

找进程:用 top 命令,找到 CPU 使用率最高的进程。

找线程:用 top -Hp [pid] 命令,列出该进程内的线程情况,按 P 键可按照 CPU 使用率排序。

拿快照:用 printf 命令,将线程 ID 转为 16 进制,用Java的 jstack 命令,打印进程快照信息。

分析:结合进程快照和线程 ID 分析,找出可能导致 CPU 高占用的代码。

推测:查看是否有线程长时间 watting 或者 blocked ,关注 watting on xXXX,这说明线程在等待锁,看是不是有死锁线程。

参看博客:JVM 内存溢出排查如何手动生成一个JVM内存溢出文件

(19)讲下 JVM 调优呢?要怎么做?

JVM 调优,本质上是调整 JVM 相关参数,以适应当前业务和部署的服务器硬件资源,以最大限度的提高业务承载量。

可以按照下面这个思路去做:

(1)获取相关参数,服务器内存配置,目前程序 JVM 的 GC 次数等等;

(2)调整 JVM 参数,堆内存的大小,年轻代与老年代的内存比例等等;

(3)部署项目验证,看 JVM 的 FullGC 次数是否有减少;

参看博客:https://zhuanlan.zhihu.com/p/16382531367


(20)8G 内存的服务器,JVM 的堆大小怎么设置?

一般设置为服务器内存的 50% ~ 75%,也就是说设置 4G 或 6G,

另外,为了避免每次垃圾回收完成后 JVM 重新分配内存,建议 -Xms(初始内存)和 -Xmx(最大内存)设置相同的值。

相关推荐
踏浪无痕5 小时前
别重蹈我们的覆辙:脚本引擎选错的两年代价
后端·面试·架构
TT哇5 小时前
【每日八股】面经常考
java·面试
Oneslide5 小时前
如何在Kubernetes搭建RabbitMQ集群 部署篇
后端
老毛肚5 小时前
黑马头条-再回首
java
专注于大数据技术栈5 小时前
java学习--8个包装类
java·学习
Lyinj5 小时前
从一个编辑校验问题谈接口设计的边界
java·spring boot·python·学习
消失的旧时光-19435 小时前
Java 线程通信:彻底理解 wait / notify(原理 + 图解 + 实战)
java·开发语言
VX:Fegn08955 小时前
计算机毕业设计|基于springboot + vue非遗传承文化管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
徐子元竟然被占了!!5 小时前
Linux-chown
java·linux·运维