怎么找到需要回收的对象
- 引用计数法
引用计数法是通过判断是否有引用指向这个对象,如果没有就说明这个对象不会再被使用了,如果有就说明这个对象可能还会继续被使用,这种通过引用是否存在的方法就叫做引用计数法,但这个方法存在一个问题就是无法解决对象循环引用的问题,因此又出现了可达性分析的方法来判断对象是否可以被会回收。 - 可达性分析
可达性分析通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。所谓的gc root,你可以理解为我们正在使用的对象,无论是栈中,静态变量还是常量,可以说是我们正在使用的,有一些文章给出GC Roots的对象包括下面几种: - 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中JNI(即一般说的Native方法)引用的对象。
官方文档里给出的不仅仅是上面几个,参考博客:https://www.zhihu.com/question/50381439:
- 1.所有已加载的类(ClassLoaderDataGraph::roots_cld_do)
- 2.所有Java线程当前栈帧的引用和虚拟机内部线程(Threads::possibly_parallel_oops_do)这个就是我们通常意义上Java代码new一个对象引用,这个对象引用所在的地方。
- 3.JVM内部使用的引用(Universe::oops_do和SystemDictionary::oops_do)基本类型的Class对象,和一些异常对象(比如NPE,OOM)等,还有类加载器,它们的成员分配在堆上面,如果没有指向成员的引用就会被当作垃圾回收,所以需要保留它们作为GC Root。
- 4.JNI handles(JNIHandles::oops_do)
GC Roots 根本思路就是:给定一个集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可到达的)对象就被判定为存活,其余对象(也就是没有被遍历到的)就自然被判定为死亡。注意再注意,本质是通过找出所有活对象来把其余空间认定为“无用”,而不是找出所有死掉的对象并回收它们占用的空间。
为什么有虚拟机栈 、本地方法栈
因为很多对象的引用都在这两个区域内。new一个对象后,对象的引用一般都在栈的局部变量中,实例对象在Java 堆中。
Java堆 分代
标记对象和压缩内存的过程在JVM中是不高效的,分配的对象越多,垃圾收集的时间就越长。但是,经过一些经验型性的统计分析表明,一个程序中大部分对象都是短命的!为了增大垃圾收集的效率,所以JVM将堆进行分代,分为不同的部分,一般有三部分,新生代,老年代和永久代。
什么时候发生垃圾回收
- eden满的时候
- 老年代满的时候
1、为什么新生代都是复制算法
因为新生代对象生存时间比较短,80%都是要回收的对象,采用标记-清除算法则内存空间碎片化严重,采用复制算法可以灵活高效,且便与整理空间。
2、老年代都是标记算法
前期一些老年代采用标记-标记算法,后续采用标记整理,标记整理算法解决来标记-清除算法的内存碎片化的问题,又解决了复制算法的两个Survivor区的问题,因为老年代的空间比较大,不可能采用复制算法,特别占用内存空间,
3、为什么要设置两个Survivor区
首先看下复制算法:Survivor区,一块叫From,一块叫To,对象存在Eden和From块。当进行GC时,Eden存活的对象全移到To块,而From中,存活的对象按年龄值确定去向,当达到一定值(年龄阈值,通过-XX:MaxTenuringThreshold可设置)的对象会移到年老代中,没有达到值的复制到To区,经过GC后,Eden和From被清空。之后,From和To交换角色,新的From即为原来的To块,新的To块即为原来的From块,且新的To块中对象年龄加1。永远有一个survivor space是空的,另一个非空的survivor space无碎片。
为什么要这么做?
- 没有Survivor区,eden区对象直接去老年代,造成老年代频繁FGC。
- 一个Survivor容易使得Survivor的空间碎片化,因为eden区又存活对象,Survivor区也有存活对象,辅助的时候就会存在碎片化空间。
- 即若只分一块Survivor,在清除Survivor区已死亡的对象时,因为此刻的Survivor区还有存活的对象,清除要比分两块Survivor麻烦,两块的情况,回收时只需将存活的对象移走,剩下的对象直接清理即可。
- 另外,分成两块Survivor,From和To分工明确,逻辑理解和技术实现较简单。
4、YGC 会STW
其实YGC过程也会STW,无论你选择什么gc,停顿都是不可避免的。CMS在初始标记和重复标记阶段会停顿,最新的G1在初始标记阶段也会停顿。并发gc只是把一(大)部分工作并发处理了,还是会停顿,只是时间短很多。另外,也并不一定停顿时间短就是最好的,并发gc会跟用户线程抢cpu。因为full gc耗时远高于minor gc,minor gc 一般几十毫秒而已。
参考博客
jvm
STW都会发生
java的gc为什么要分代?
Java中什么样的对象才能作为gc root,gc roots有哪些呢?
什么是GC Roots