1. 导读部分
写在开始 : 本文是学习 [黑马程序员] JVM 面试专题 的学后记录, 仅仅用作个人复习准备面试用。
附学习地址 : https://www.bilibili.com/video/BV1yT411H7YK?p=116&vd_source=08ac522c6603c56e243d5e129a309a60
2. JVM 组成
2.1 程序计数器
个人回答 : 程序计数器是运行时数据区的一部分,用来指向执行的指令的位置,唯一不会发生OOM的地方;
2.2 堆
个人回答 : 堆一般是运行时数据区空间最大的地方,存放的对象和数组;堆是线程共享的,也是垃圾回收的主要对象;堆分为新生代和老年代,新生代又分为eden区和s from 和 s to区,一般比例是8:1:1;
- 年轻代分为三部分,Eden区和两个大小严格相同的 Survivor 区
- 老年代主要保存生命周期长的对象,一般是一些老对象
2.3. 方法区
个人回答 : 方法区也是线程共享的,7及之前是永久代,8及之后是元空间,
:::info
:::
2.4. 直接内存
个人回答 : 没听过
2.5 虚拟机栈
个人回答 ; 虚拟机栈也是运行时数据区一部分,每个线程私有,生命周期随着线程的产生和销毁而变化,当内存空间不够会抛出 stackoverflow 异常,也可能有情况抛出 OOM 异常,存储的是方法出口,局部变量等,栈内存按照先进后出的顺序执行;
:::info
:::
2.6 垃圾回收是否涉及栈内存
个人回答 : 垃圾回收主要针对堆内存,栈是方法执行完毕弹栈释放,内存自动释放
2.7 栈内存分配越大越好吗
个人回答 : 不一定
:::info
:::
2.8 方法内的局部变量是否线程安全
个人回答 : 不一定
:::info
:::
2.9 栈内存溢出的情况
个人回答 ; 栈帧过大
// 1 栈帧过多导致栈内存溢出,典型问题 : 递归调用
// 2 栈帧过大导致栈内存溢出
public static void m1(){
m1(); // 抛出 java.lang.StackOverflowError异常
}
栈堆的区别是
个人回答见下表:
区别 | 栈 | 堆 |
---|---|---|
大小 | 一般是1024K | 随对象的大小而定,一般大于栈 |
私有 | 每个线程私有 | 共享区域 |
生命周期 | 随方法调用一样 | 对象创建存在,垃圾回收可能消失 |
异常类型 | 两种 SOF OOM | 只有 OOM |
存啥 | 存局部变量和返回地址等 | 对象和数组等 |
垃圾回收 | 无 | 有 |
物理地址 | 连续,性能快 | 不连续,性能慢 |
3. 类加载器
3.1 类加载器是什么?有哪些
个人回答 : 类加载器就是对类的信息进行加载,有引导类加载器,扩展类加载器,系统应用类加载器以及自定义类加载器等几种
:::info
:::
3.2 双亲委派模型
个人回答 : 类的加载优先找父类加载器进行,首先是引导类,其次是扩展类,然后是系统应用类加载器,最后是自定义的,为了保证安全,核心类库不被篡改;
:::info
:::
3.3 JVM为什么用双亲委派机制
:::info
:::
3.4 类装载执行过程
个人回答 : 准备 -> 连接 -> 初始化 太长记不清楚了,其中连接分为验证解析引用
:::info
:::
4. 垃圾回收
强引用,软引用,弱引用,虚引用
个人回答 : 强不会被回收,软,当内存不够时会回收,弱,内存无论是否够都回收,虚
:::info
:::
:::info
:::
static class Entry extends Weakreference<ThreadLocal<?>>{
Object value;
Entry(ThreadLocal<?> k,Object v){
super(k);
value = v; // 强引用,不会被回收
}
}
判断对象可被垃圾器回收
个人回答 : 引用计数 可达性分析
:::info
:::
:::info
引用计数:
一个对象被引用一次,在当前对象头上递增一次引用次数,如果对象引用次数0,则可回收;
当对象出现了循环引用,则失效;引发内存泄漏;
可达性分析:
:::
JVM垃圾回收算法
个人回答 : 标记清除,标记整理(针对老年代),复制算法(新生代常用)
:::info
:::
分代回收
个人回答 : 主要针对不同的内存区域采用不同的垃圾回收方式,垃圾回收器
:::info
- 堆区域划分
- 堆被分2份:新生代和老年代(1:2)
- 新生代内部分为三部分 Eden区 幸存者区 survivor(from和to) 8:1:1
- 对象分代回收策略
- 新建对象,去Eden
- 当 Eden内存不足,标记Eden和from的存活对象
- 复制算法到 to区,完毕后 Eden和from 内存释放
- 一段时间Eden内存又不足,标记Eden区to区存活对象,复制到 from区
- 幸存区对象熬过15次回收,晋升到老年代(大对象提前晋升)
MinorGC 新生代垃圾回收,暂停时间短 (STW)
Mixed GC 新生代和老年代部分区域垃圾回收 G1特有
FullGC 新生代老年代完整垃圾回收,暂停时间长,尽力避免
:::
G1 垃圾回收器
个人回答 :
- 应用于新生代和老年代,9以后默认使用 G1
- 划分多个区域,每个区域可充当 eden survivor old humongous(专为大对象准备)
- 采用复制算法
- 响应时间和吞吐量兼顾
- 分三个阶段 新生代回收(stw)、并发标记(重新标记 stw)、混合收集
- 如果并发失败(回收赶不上新建对象速度)触发Full GC
哪些垃圾回收器
串行垃圾收集器
Serial GC | Serial Old GC
并行垃圾收集器
Parallel Old GC | parNew GC
CMS(并发)垃圾收集器 作用在老年代
5. JVM 实战
JVM 调优参数哪里设置
:::
JVM 调优参数有哪些
堆空间大小 Xms堆初始化大小 Xmx堆最大大小
- 最大大小默认值物理内存25%,初始大小1/64;
- 堆太小,导致频繁的垃圾回收,stw,
- 堆内存打肯定好的,存在风险,假如 FullGC 扫描整个堆空间,stw时间长
- 最终: 尽量大,也需考虑当前计算机其他程序内存使用情况;
虚拟机栈的设置
- 每个线程默认1M,存放栈帧,调用参数,局部变量等,一般256K就够用 -Xss128K
- 通常减少线程的堆栈,可产生更多线程,实际受限于操作系统;
Eden和Survivor大小比例
年轻代晋升老年代阈值
**设置垃圾回收器 **
JVM 调优工具
内存泄漏排查思路
内存泄漏通常指堆内存,一些大对象不被回收的情况
- 通过 jmap 或设置 jvm 参数获取堆内存快照 dump
- 通过工具, VisualVM 分析 dump 文件,可加载离线的 dump 文件
- 通过查看堆信息情况,大概定位内存溢出的代码位置
- 找到对应代码,通过阅读上下文,进行修复即可
CPU 飙高排查方案和思路
- 找线程信息
ps H -eo pid,tid,%cpu | grep 40940
分析得到进程40940里 4.950占用cpu 较高
- 根据线程id 找有问题的线程 ,进一步定位到问题代码的源码行号
jstack 40940
40940是进程id,40955是线程id
十进制转十六进制
printf “%x\n” 4.955
:::info
- top命令查看cpu占用情况
- top命令查看后,可查哪个进程占用cpu较高
- ps命令查看进程的线程信息
- jstack命令查进程的哪些线程出现问题,最终定位问题;
:::