什么样的对象需要回收
如果对象已经死亡了,就可以进行回收,判断方式如下
1).引用计数器:给对象添加一个计数器,有地方引用,就+1,当引用失效,就-1。当计数器为0时,判断对象不能再使用,但是当对象相互引
用的时候无法进行GC
1).可达性算法:从GC Roots开始,到对象之间有引用链相连,就是可达的。HotSpot采用可达性算法,商用虚拟机没有采用引用计数器
GC Roots有哪些:
1).局部变量表中引用的对象
2).栈帧中常量引用的对象
3).栈帧中static引用的对象
4).JNI引用的对象
5).类加载器
6).Thread
垃圾回收算法
1).标记清除:对不具有可达性的对象进行标记,然后进行统一回收,最基本的GC算法
缺点:标记和清除两个阶段效率都不高,会产生大量不连续的内存碎片,在给大对象分配内存的时候,可能无法找到连续的内存,而不得不提
前进行GC
2).复制:把内存分成相同两部分,每次使用一部分,GC的时候把可用对象复制到另一边,然后把使用的一边直接清理掉,不会产生内存碎片,
但是直接浪费了一半内存,代价太巨大了。
3).标记整理:如果对象存活率比较高的时候,复制算法的效率就会降低,而且需要有额外的空间进行分担担保,老年代就不可能使用对存活
对象进行标记之后,然后把存活的对象都向一端移动,直接清理边界以外的内存
4).分带收集:当前商用VM采用的GC算法,新生代使用复制算法,老年代使用另外两个。
商用虚拟机的新生代都是采用复制算法,新生代的对象98%生命周期都很短,每次使用Eden Space和From Survivor,把这两块区域存活的对象
复制到To Survivor,然后清理内存,Eden Space:From Survivor=8:1,这样每次只会浪费10%内存,当To Survivor内存空间不够的时候,使用老年
代。
HotSpot的算法实现
可达性分析从GC Roots节点找引用链时,会发生GC停顿,是指分析期间停止其他所有的线程,如果分析过程中对象引用关系还在变化,准确性
无法得到保证。
主流jvm采用准确式GC,HotSpot中通过一组OopMap的数据结构知道哪些地方存放着对象引用,就可以快速、准确完成GC枚举。
新生代和老生代默认的空间占比分别是是1/3、2/3。
复制算法的执行流程如下
1、把Eden + From Survivor存活的对象放入To Survivor区;
2、清空Eden和From Survivor分区;
3、From Survivor和To Survivor分区交换。
4、每次在From Survivor到To Survivor移动时都存活的对象,年龄就+1,当年龄到达15(默认配置是15)时,升级为老生代。大对象也会
直接进入老生代。
老年代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。
方法区回收:(jdk1.8之前永久带实现了方法区,而在jdk1.8废弃永久带,通过本地内存实现)
对方法区回收也可以说是对永久带回收,主要针对两部分:
1、废弃的常量:例如一个字符串"abc"在常量池中,但是没有任何String对象引用它,进行GC的话,就需要被清理出常量池,类、方法、字段
的符号引用也是相同的
2、无用的类:
1).该类的所有实例都已经被GC
2).加载该类的ClassLoader被GC
3).该类对应java.lang.class对象没有被任何地方引用,也就是无法通过反射得到该类的方法。
引用:
就是本身保存的数据是另一块内存的起始地址
分类:
1、强引用:类似于new一个对象对应的引用,只要具有可达性,不会被回收
2、软引用:软引用关联的对象,在内存溢出发生以前,这些对象会被回收。可以通过SoftReference实现软引用
3、弱引用:只能活到下次GC之前,当进行垃圾回收的时候,它肯定死了。可以通过WeakReference实现软引用
4、虚引用:最弱的一种引用关系。为对象设置虚引用唯一目的就是能在对象被GC的时候收到一个系统通知。可以通过PhantPhantomReference实现软引用