(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(最大内存)设置相同的值。