文章目录
前言
你知道什么对象会被称只为垃圾对象,垃圾对象又是怎么进行回收的。本文重点对面试的问题进行介绍,祝愿每位程序员都能顺利上岸!!!
一、哪些是垃圾对象?
在程序了创建的对象,只要不被使用了,就应该进行回收,然后释放对象占用的内从空间;那么怎么判断一个对象是否还有在被使用。
1.1 引用计数法
.个对象被引用了一次,在当前的对象头上递增一次引用次数,如果这个对象的引用次数为0,代表这个对象可回收。
对象出现了循环引用,此时引用计数法会失效。
1.2 gcroot 根可达
通过gcroot 根进行连接的都是可用的对象。
哪些对象可以作为gcroot
二、你都知道哪些垃圾回收算法
现在已经可以通过gcroot 可达法,得到所有被使用的对象,哪些不可达的对象就是垃圾对象,jvm对这些垃圾对象是怎么进行回收的呢。
2.1 标记清除算法
标记清除算法,是将垃圾回收分为2个阶段,分别是标记和清除
- 1.根据可达性分析算法得出的垃圾进行标记
- 2.对这些标记为可回收的内容进行垃圾回收
2.2 标记清除整理算法
在标记清除的基础上,将对象进行一端的移动,从而,解决了标记清除算法的碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也有有一定的影响。
2.3 复制算法
将存活的对象,复制到另外一个内存中去。
三、你知道分代回收吗
针对jvm 堆中,不同的代使用不同的垃圾回收算法。新生代使用复制算法,老年代使用标记清除或者标记清除整理算法。
3.1 新生代复制算法
- 新创建的对象,都会先分配到eden区
- 当伊甸园内存不足,标记伊甸园与 from(现阶段没有)的存活对象
- 将存活对象采用复制算法复制到 to 中,复制完毕后,伊甸园和 from 内存都得到释放
- 经过一段时间后伊甸园的内存又出现不足,标记eden区域to区存活的对象,将存活的对象复制到from区
- 当幸存区对象熬过几次回收(最多15次),晋升到老年代(幸存区内存不足或大对象会导致提前晋升)
3.2 老年代标记清除或者标记清除整理算法
老年代通过gcroot 扫描到所有的存活对象,没有被扫描到的对象就是垃圾对象,对这些对象进行清除,清除后可以进行整理,清除内存碎片。
四、你都知道哪些垃圾回收器
jvm 中你都知道哪些垃圾回收器,它们的工作原理你有过了解吗,你知道它们的适用场景吗,如果让你选择垃圾回收器,你会怎么选择;
4.1 串行垃圾收集器
Serial和Serial Old串行垃圾收集器,是指使用单线程进行垃圾回收,堆内存较小,适合个人电脑
- Serial 作用于新生代,采用复制算法
- Serial Old 作用于老年代,采用标记-整理算法垃圾回收时,只有一个线程在工作,并且java应用中的所有线程都要暂停(STW),等待垃圾回收的完成。
4.2 并行垃圾收集器
Parallel New和Parallel Old是一个并行垃圾回收器,JDK8默认使用此垃圾回收器;
- Parallel New作用于新生代,采用复制算法
- Parallel Old作用于老年代,采用标记-整理算法垃圾回收时,多个线程在工作,并且java应用中的所有线程都要暂停(STW),等待垃圾回收的完成。
4.3 CMS(并发)垃圾收集器
CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器,该回收器是针对老年代垃圾回收的,是一款以获取最短回收停顿时间为目标的收集器,停顿时间短,用户体验就好。其最大特点是在进行垃圾回收时,应用仍然能正常运行。
采用的是"标记-清除算法",整个过程分为4步:
(1)初始标记 CMS initial mark 标记GC Roots直接关联对象,不用Tracing,速度很快
(2)并发标记 CMS concurrent mark 进行GC Roots Tracing
(3)重新标记 CMS remark 修改并发标记因用户程序变动的内容以及扫描标记在第二阶段产生的新对象;
(4)并发清除 CMS concurrent sweep 清除不可达对象回收空间,同时有新垃圾产生,留着下次清理称为浮动垃圾;
4.4 G1垃圾收集器
应用于新生代和老年代,在JDK9之后默认使用G1
- 划分成多个区域,每个区域都可以充当 eden,survivor,old,humongous,
- 其中 humongous 专为大对象准备,采用复制算法,
- 响应时间与吞吐量兼顾,分成三个阶段:新生代回收、并发标记、混合收集,
- 如果并发失败(即回收速度赶不上创建新对象速度)会触发 FuIl GC
你知道G1 的回收过程吗
4.4.1 年轻代回收
- 初始时,所有区域都处于空闲状态
- 创建了一些对象,挑出一些空闲区域作为伊甸园区存储这些对象当伊甸园需要垃圾回收时,挑出一个空闲区域作为幸存区,用复制算法复制存活对象,需要暂停用户线程
eden 复制算法,回收E和S 并晋升存活时间久的对象,将对象放入到新的S
4.4.2 对象晋升老年代
随着时间流逝,伊甸园的内存又有不足;将伊甸园以及之前幸存区中的存活对象,采用复制算法,复制到新的幸存区,其中较老对象晋升至老年代。
4.4.3 老年代回收
- 当老年代占用内存超过阈值(默认是45%)后,触发并发标记,这时无需暂停用户线程;
- 并发标记之后,会有重新标记阶段解决漏标问题,此时需要暂停用户线程。这些都完成后就知道了老年代有哪些存活对象,随后进入混合收集阶段。
- 此时不会对所有老年代区域进行回收,而是根据暂停时间目标优先回收价值高(存活对象少)的区域(这也是 Gabage First 名称的由来)。
混合收集阶段:
4.5 让你选择垃圾回收器你会参考哪些
4.6 你有过jvm 调优的经历吗
- 为了防止堆内存在最大和最小内存之间扩缩容消耗时间,我们初始化堆内存是通常将最小和最大堆内存设置成一样的;
- 对于虚拟机栈,每个线程默认是1M ,但一般256KB 就够用;
- 调整新时代晋升老年代的年龄
- 我们也通过一些命令和工具进行调优
4.6.1 内存泄漏排查思路
程序出现 java.lang.0utOfMemoryError Create breakpoint :TJava heap space
1)通过jmap指定打印他的内存快照dump(Dump文件是进程的内存镜像。可以把程序的执行状态通过调试器保存到dump文件中)
使用jmap命令获取运行中程序的dump文件
jmap -dump:format=b,file=heap.hprof pid
使用jvm参数获取dump文件,有的情况是内存溢出之后程序则会直接中断,而imap只能打印在运行中的程序,所以建议通过参数的方式的生成dump文件
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/app/dumps/
2)通过工具,VisualVM去分析dump文件,VisualVM可以加载离线的dump文件文件–>装入—>选择dump文件即可查看堆快照信息
3)通过查看堆信息的情况,可以大概定位内存溢出是哪行代码出了问题,找到对应的代码,通过阅读上下文的情况,进行修复即可。
4.6.2 java 进程导致cpu 彪高
1)使用top命令查看占用cpu的情况
2) 通过top命令查看后,可以查看是哪一个进程占用cpu较高,上图所示的进程为:40940
3) 打印进程中所有的线程查看进程中的线程信息
ps H -eo pid,tid,%cpu l grep 40940
4)线程id 转换为16进制,然后通过jstack 查看堆信息,找到有问题的代码行
总结
本文档对JVM 的垃圾对象确定,以及垃圾回收算法,垃圾回收器,jvm 的调优 面试题进行总结。