GC收集
- 收集的意义:回收已死的对象,垃圾收集是否限定在堆上?方法区的空间回收是否属于GC的范围
虚拟机栈、本地方法栈、程序计数器都是随着线程而生,随着线程而亡,因此不需要专门回收。方法区的回收价值不高,但是GC也可能会回收方法区的内容,回收的内容包括废弃的常量和无用的类型是否回收方法区内容可以通过-Xnoclassgc控制,GC主要是针对堆上的对象进行回收的。 - 如何判断对象已死?finalize的意义是什么?引用计数法,循环依赖问题如何解决?
引用计数法和根枚举算法。finalize意义是给对象一个逃脱的机会。循环依赖问题可以通过智能指针解决,就是如果两个对象互相引用,那么一个是强引用,一个是弱引用,但是这样会带来野指针的问题,如果弱引用引用的强引用被回收了,那么需要重新创建出来。
https://www.jianshu.com/p/90bc169cc192 - 、根枚举算法,GC Roots都有哪些?虚拟机如何得到GC Roots的对象集合?如何从GC Roots向下继续搜索?
GC Roots包括虚拟机引用的对象,譬如各个线程方法堆栈中引用的局部变量、参数等;方法区直接引用的对象,本地方法栈引用的对象;虚拟机内部直接引用的对象,比如基本数据类型的Class对象,异常对象等;同步锁持有的对象;反映虚拟机内部的JMXBean、JVMTI中注册的函数、本地代码缓存等。虚拟机是通过OopMap将对象什么偏移量是什么类型数据保存下来,这样只要扫描OOpMap就可以得到GC Roots,从根节点搜索的过程就是不断出栈,再将他所对应的引用入栈的过程,直到栈为空,就扫描结束。 - OOPMap是什么?如何提高搜索的速度?安全点的意义是?
在类加载完成后,虚拟机会将对象内偏移量保存的类型算出来,在即时编译的时候,也会在特定位置记录栈和寄存器哪些位置是引用计算出来。这样就不需要对线程上下文和全局的常量进行搜索。
导致OopMap变化的指令非常多,如果每条指令都记录就会花费很多空间,所以虚拟机是在特定位置记录的,也就是安全点,安全点需要满足程序能够长时间停顿。而且垃圾收集的时候需要都跑到最近的安全点,然后停顿下来,目前的方式主要有抢占式中断和主动式中断,目前虚拟机采取的方式是主动式中断,也就是线程在执行过程中会不断轮训一个标志位,如果为真,就跑到最近的安全点停顿下来,这个轮训标志位的操作是指令级,避免对性能影响较大。 - 分代回收如何解决不同代之间互相引用问题?
使用记忆集,也就是卡表来实现的。具体就是把内存空间分成多个卡页,只要卡页上任何对象被引用上,那么就设置为脏页,这样就需要重新扫描。卡表避免了扫描老年代所有空间。卡表的维护是通过写屏障来实现的,可以看成是写指令上的AOP操作 - Remember Set和G1中的类似的集合不同之处,数据结构不同?
主要原因是G1中的记忆集需要维护多个区的引用关系。G1的更复杂。 - 不同的收集算法复制算法、标记整理、标记清除的优缺点,在收集器的实现中分别如何使用这些算法的?
标记清除,主要是CMS使用的收集器,缺点是会造成内存碎片,优点是回收快,适合老年代的回收。
标记整理,标记,整理到一端。需要移动对象到另一端,如果存活率较低,也很影响性能,但是不会产生内存碎片,且能够充分利用资源
复制算法,复制算法适用于存活率低的场景,复制效率低,空间利用率低
分代回收算法,按照对象的存活年龄分为新生代和老年代。新生代中继续划分空间,通常是8:1的比例,剩下的空间用以放置存活对象。8:1这个比例是因为据统计,95%的对象都是处于朝生夕灭的。对象首先在eden区分配,然后s区分为from和to区,刚开始from区是空的,然后minor GC之后,eden区对象被回收,eden区存活对象进入to区,from区中无用对象也会被回收,from区的存活对象根据年龄判断是否进入老年代,否则就进入to区。minor GC之后,to和from区互换身份。 -
具体的收集器的实现,他们都在什么场景使用?怎么搭配使用?当前JDK1.8版本使用的收集器是什么?
image.png
CMS:对响应时间的重要性需求 大于对吞吐量的要求,能够承受垃圾回收线程和应用线程共享处理器资源,并且应用中存在比较多的长生命周期的对象的应用。
1.8默认收集器Parallel Scavenge和Parallel Old(PS Mark Sweep) - CMS的实现细节,每个阶段初始标记,并发标记,重新标记,并发标记,并发重置,每个阶段的意义是什么?
初始标记:遍历GC ROOTS直接可达的对象并将其压入标记栈(mark-stack)。标记完之后恢复应用程序线程。
并发标记:这个阶段虚拟机会分出若干线程(GC 线程)去进行并发标记。标记那些GC ROOTS最终可达的对象。具体做法是推出标记栈里面的对象,然后递归标记其直接引用的子对象,同样的把子对象压到标记栈中,重复推出,压入。直至清空标记栈。这个阶段GC线程和应用程序线程同时运行。
重新标记:因为在并发标记阶段,可能会存在漏标的对象。所以单纯的并发标记操作并不能保证GC的正确性,所以还需要额外的操作,这个操作就是write barrier。即当赋值引用时,如果赋值的对象还没有被标记,将标记该对象将其压入标记栈。
重新从当前的GC ROOTS和指针更新的区域出发(mod-union table)再进行一次标记,所以这个过程被叫作重新标记。需要注意的是:已经标记的对象是不会再遍历一次,标记线程识别对象在并发阶段已经标记过了,就会跳过该对象。所以重新标记只会遍历那些新增没有标记过的活动对象和其间有指针更新的活动对象,如果指针更新频繁,重新标记很有可能会遍历新生代中的大部分甚至全部对象。所以如果重新标记阶段很慢,可以启动一次YGC,来减少并发标记的工作量减少其停顿时间。
并发清除:老年代的对象通常是存活时间长,回收比例低,所以采用的回收算法是标记-清除。这个阶段GC回收线程是遍历整个老年代,遇到没有被标记的对象(垃圾)就清空掉相应的内存块,并加入可分配列表。遇到被标记的对象保持原来的位置不动,只是重置其标记位,用于下一次GC。 - 关于CMS有哪些参数可以调优?是否线上触发过Full GC,CMS停顿时间过长的原因可能有哪些?怎么排查?调节触发GC的阈值
https://blog.csdn.net/luzhensmart/article/details/105876603 - 内存碎片问题?浮动垃圾问题,吞吐量的影响
不过需要的注意的是标记-清除会带来的影响就是内存碎片化,当内存中碎片过多时,就不得不进行内存压缩了。如果并发收集所回收到的空间赶不上分配的需求,就会回退到使用serial GC的mark-compact算法做full GC,这时候GC带来的暂停可能会比较长,这种情况又被称为并发模式失败(Concurrent Mode Failure)
https://zhuanlan.zhihu.com/p/54286173 - Minor GC和Full GC区别,分别怎么触发?对应用的影响?
Eden区满了空间分配失败会触发Minor GC,老年代空间不足、空间担保失败、大对象都可能会触发Full GC
G1收集器引进的意义?G1收集过程,设计原理?存在哪些问题,如何解决这些问题?G1触发Full GC的原因
G1调优方向
Full GC和OOM时,我怎么知道是哪一段代码引起的内存溢出和泄漏
GC日志怎么看?关注哪些?
JVM调优参数相关:
虚拟机相关:影响整个虚拟机
XMX:最大堆空间
XMS:最小堆空间
堆分区相关:动态调整适配机器
XX:NewRatio 新生代的比例
XX:SurvivorRatio S1S0和edun区的比例
GC机制相关:动态调整适配是否需要低延迟还是高吞吐
XX:CMSInitiatingOccupancyFraction=70
网友评论