记录关于GC不得不知道的知识点~~
什么是安全点、安全域?
- 当一个线程挂起或者一段代码,引用关系不会发生变化,那么就是进入安全域,该线程满足GC安全要求
- 当java程序需要进行一次GC的时候,通知线程进入安全区域,如果线程早已经在安全域,该线程通过安全检查;如果线程还在运行当中,那么接下来线程将会选择一个安全点停下。安全点一般是方法调用、循环调用和异常跳转。设置安全点现在一般是主动式中断,设置中断标志方式。
永久代
永久代在1.8之前,存储类的信息、常量池、方法数据、方法代码等,逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
永久代在1.8之后去除,新增本地内存MetaSpace,本质上与永久代类似,但是一个是堆内存一个是本地内存。
新生代
新生代的的内存大小分配比例为什么默认是8:1:1?
- 因为一般而言。新生代中,每次发生GC,对象大部分都是能够被回收的,survior区域不需要很大。8:1:1是一个适当的比例。
新生代可分配的内存大小是多少?
- 新生代中,可以供内存分配的大小是eden+1个survior区域大小,优先分配到eden区域
老年代
各个代的大小分配应该为多少比较合适?
- 按照经验,应该按照程序的活跃数据大小来确定堆的大小。那么什么是活跃数据呢?在程序稳定运行之后,分析gc日志,取多次日志中Full GC之后老年代数据大小的平均值,计算出活跃数据大小。、
- 堆大小=3~4倍活跃数据大小
- 新生代大小=1~1.5倍活跃数据大小
- 老年代=求差
什么对象进入老年代?
- 大对象直接进入老年代,参数设置-XX:PretenureSizeThreshold
- 达到年龄阈值的对象进入老年代,年龄指的是对象每经过一个Minor gc,年龄加一,新建对象年龄为0。年龄阈值设置参数为-XX:MaxTenuringThreshold。大于该值,对象进入老年代。
- 动态年龄判断,满足则进入老年代。如果survivor空间中,相同年龄的对象大小大于survivor空间大小的一半,那么凡是大于该年龄的对象进入老年代。
GC
什么时候会触发full gc?
-
在1.8之前,永久代控件不足时,触发full gc
-
CMS垃圾回收器执行来及回收时,出现***concurrent mode failure*** 时,触发full gc。什么是concurrent mode failure ?《深入理解java虚拟机》有详细解析,cms无法处理浮动垃圾,cms只有在remark过程是stw的,在实际清理垃圾过程是和用户线程并发执行的,导致回收时,可能还产生新的垃圾,只能在下一次gc中回收。这部分浮动垃圾在并发回收时,需要分配内存,**如果无法提供足够内存给浮动垃圾,那么就会出现concurrent mode failure,同时触发full gc,老年代改用串行回收器回收。**为了保证有充足内存提供给浮动垃圾,所以cms一般不会等到100%才进行垃圾回收。
-
***空间分配担保***失败时,触发一次fullgc。在新生代分配对象前,先检查老年代的最大连续可用空间是否大于新生代所有对象的大小总和。
- 如果是则安全分配;
- 如果否,则继续检查是否允许空间分配担保失败。
- 如果允许,继续检查历代晋升到老年代的对象的平均大小
- 如果最大连续可用空间大于这个值,那么则可以尝试新生代分配,如果分配失败,最终还是要触发full gc,饶了大圈子。这种情况称之为HandlerPromotionFailure,担保失败。
- 如果否则则触发full gc。
- 如果不允许,那么直接触发full gc。
- 如果允许,继续检查历代晋升到老年代的对象的平均大小
- 主动触发Full GC(执行jmap -histo:live [pid])来避免碎片问题。
什么对象会被回收?
- 最根本的就是可达性分析,是否存在GC root直接或者间接引用该对象,没有的话就需要被回收,有的话在具体分析引用类型。
- 什么是GC root?
- 虚拟机栈中的本地变量表中引用的对象,也就是方法变量对象
- 在方法区中的类静态常量引用的对象
- 本地方法栈中的JNI引用的对象
- 什么是GC root?
- 如果对象有Gc root可达,那么就需要分析对象的引用类型。
- 强引用,只有在没有gc root可达时回收
- 软引用,在gc一次之后,内存依然不足,在将要发生内存溢出异常之前,针对软引用的对象,再一次进行回收,如果还是不足,就内存溢出。以上分析不管有没有gc root可达。
- 弱引用,在垃圾回收时必定纳入回收范围,不管有没有gc root可达。
- 虚引用,虚引用完全不影响对象的生命周期。