1.如何确认是垃圾:
a 引用计数法
b 可达性分析法
2.可达性分析法哪些可以作为gc roots (全局引用:常量或静态变量/执行上下文:栈帧中的本地变量表)
a 虚拟机栈中引用的对象(局部变量,临时变量,参数)
b 方法区中静态属性引用的对象(静态变量)
c 方法区中的常量引用的对象
d 本地方法栈
e 虚拟机的内部引用(常驻的异常对象nullException,)系统类加载器
3. 引用:
a 强引用:通过new等生成的对象,只要强引用还在,垃圾回收就不会回收该对象
b 软引用:一些有用但非必须的对象,经过一次垃圾回收之后,如果内存还是不够,第二次会回收该内存
c 弱引用:类似软引用,只是垃圾回收时,无论内存是否充足,都会回收该内存,
d 虚引用:只是为了垃圾回收后收到一个系统通知
4 可达性分析:如果一个对象经过可达性分析法,标识为不可达,是否会立即回收呢?
不会,需要经过标记,筛选;如果对象不可达,进行标记;然后进行筛选,筛选的条件是是否需要执行finalize方法,如果没有覆盖该方法或者虚拟机已经执行过该方法,则没有必要;否则,该对象会被放入f-queue队列,并在稍后虚拟机会主动建立一个低调度低优先级的finalize线程区执行finalize方法。如果不想被回收,需要和引用链上任意一个对象建立链接。
5 方法区的回收:
虚拟机不要求必须对方法区进行垃圾回收,因为对该区域回收的效率较低,方法区重要是针对废弃的常量和不再使用的类型;
a 没有任何对象使用的常量,就会被回收
b 类型:类型的回收需要满足3个条件才允许回收
1 所有类实例都被回收
2 该类的类加载器被回收,除非被精心设计过的(osgi,jsp重加载),否则很难达成
3 该类的java.lang.Class对象没有在任何地方被引用,而且任何地方都无法通过反射访问该类
6 分代收集理论:弱分代理论,强分代理论
a 弱分代理论:绝大多数对象都是朝生夕死
b 强分代理论:熬过多次回收的对象难以销亡
7 分区回收(新生代,老年代):假如进行一次局限新生代回收,但新生代对象可能被老年代引用,那对新生代回收否需要对整个老年代也进行gc roots或者记录哪些对象是否存在哪些跨代引用?
跨代引用假说:假如新生代被老年代对象引用,当垃圾回收时,该对象无法被回收,记过多次垃圾回收后,也会跨入老年代,这样就没有跨代引用的情况了,所以没有必要浪费空间去维护;只需要在新生代维护一个记忆集,把老年代分为多个小快,标示出包含跨代引用的小块内存,当新生代进行垃圾回收时只扫描包含跨代引用的小块内存。
8 标记-清除算法:标记,清除缺点是如果有大量对象需要清除,会有大量的标记,清除操作,效率低;而且会大量的内存碎片,如果有个大对象需要分配内存,可能没有足够的内存会导致回收
9 标记-复制(适合新生代,对象存活率高,需要复制的对象多,效率地下):当前商业的虚拟机都是按照8:1:1的比例分为eden,survivor,将其中eden和一块survivor存活的对象复制到另一块survivor中,但10%的内存无法确定能够满足内存的分配,所以需要依赖其他区域进行担保分配,如果没有足够的空间存放上一次新生代收集下来的存活对象,通过担保分配直接进入老年代。
10 标记-整理:和标记-清除一样,只是标记后不是直接对可回收对象进行清理,而是将所有存活对象向一端移动,然后清除掉边界意外的内存;与清除相比,一个是移动的,一个是不移动的,
针对老年代这种存活对象大的区域,移动存活对象并更新对应所以引用这些对象的地方会是极为负重的操作,但不移动又会有内存碎片
11 根节点枚举是否必须停顿所有程序?是的,当前垃圾回收器已经可以做到查找引用链的过程与用户线程一起并发,但根节点枚举必须在一个能保障一致性的快照中才得以进行,所谓的一致性是指某个时间点上,不会出现根节点分析过程中,根节点集合的对象引用关系还在变化中,这样分析的结果也无法确保准确性。这也是垃圾回收过程必须停顿所有用户线程的一个重要原因,即使是号称停顿时间可控或几乎不会发生停顿的cms,g1,zgc等收集器,根节点枚举时也必须停顿。
12 方法区中包含的常量,静态变量可能很多很多,如果逐个检查以这些为起源的引用肯定消耗很长时间,那虚拟机是如何做的?
在hotspot的解决方案中,使用的是一组成为oopMap的数据结构来实现的,一旦类加载完成时,hotspot会把对象内什么偏移量上是什么类型的数据计算出来,在即时编译过程中,也会在特定位置记录里和寄存器里哪些位置是哪些引用,这样收集器扫描时就可以直接获取这些信息,不需要真正一个不漏地从方法区等gc roots开始查找。通过oopMap能够快速的完成gc roots,但是引起oopMap的指令很多,如果为每条指令都生成对应的oopMap,那空间成本高昂,hotspot也确实没有这样实现,前面说到的特殊位置,其实对应的就是一个安全点,程序执行的代码指令流并不是任何位置都可以停顿下来进行垃圾回收,必须达到安全点后才能停顿,安全点位置的选取基本上是以“是否具有让程序长时间执行的特征”。另外一个问题是,任何让程序都能跑到最近的安全点?有两种方式:抢断式和主动式,抢断式是在垃圾回收发生时,主动把所有线程中断,如果发现线程不再安全点,则恢复线程执行;主动式是当垃圾回收需要中断线程运行时,不直接对线程操作,而是设置一个标志码,线程执行过程中不断去轮训这个标志,如果标志为true,说明自己在最近的安全点上主动中断挂起。安全点的设置保障了现在在不太长的时间里会遇到可进入垃圾收集过程的安全点,但如果线程是不执行的时候呢,即sleep或blocked状态下,所以引入了安全区域;当用户线程进入到安全区域时,首先会标记自己已经进入到安全区域了,当垃圾回收进行收集时就不用管在安全区域中的线程,当线程要离开安全区域时,它要检查虚拟机是否已经完成了根节点枚举,如果没有完成,则一直等待。
13 记忆集和卡表:在跨代引用时讲到垃圾回收器在新生代建立了记忆集,用以避免把整个老年代加入gc roots扫描。
原文:https://www.cnblogs.com/yangyanping-blog/p/12420829.html