引用计数法
对象之间有引用,那么引用计数+1,引用消失,引用计数减一,引用计数为0的时候,对象可以释放。
如果是相互引用的对象,无法释放,例如:A引用B,B引用A,他们的引用计数都是2,把A指向null,B指向null,引用计数都只能减一。
可达性分析
从GC Roots出发,判断对象是否可达。如果创建A:Object a = new Object(),B:Object b = new Object(),a=b,此时是把a指向了b的对象B,那么对象A不再是可达对象,会被垃圾回收器回收。
GCRoots
- 方法区:类静态变量引用的对象
- 方法区:常量引用的对象
- 虚拟机栈布局变量表中引用的变量
- 本地方法栈JNI引用的变量
引用类型
强引用:直接引用的对象
软引用:SoftReference包裹的对象,如果系统内存不足,软引用对象会被回收,例如发生OutOfMemoryError,并且捕获了这个异常的时候,在之后,通过SoftReference获取包裹对象,就会得到一个null,前提是没有其他引用。
弱引用:WeakReference包括的对象,gc扫描的时候,就会回收,前提是没有其他引用。
虚引用:如果被回收了,会发出通知,基本用不着。
回收算法
复制回收算法
把内存一分为二,gc回收的时候,把可达对象赋值到另外一块内存区域,然后之前的内存区域做一次整体清除。
优点是速度快,效率高,缺点是利用率比较低,适用于新生代中,存活对象比较少的情况。
标记清除
标记所有可达对象,清除其他不可达的对象。
优点是空间利用率100%,缺点是内存碎片比较多,分配大对象,无法分配
标记整理
标记所有可达对象,然后整理可达对象,最后清除所有的不可达对象。
优点是不会产生内存碎片,缺点是对象需要移动,消耗性能,可以产生停顿
GC内存管理
主要分为新生代和老年代,默认比例是1:2,新生代中分为Eden区,Survivor From,
Survivor To区域,默认比例是8:1:1,可以通过Vm options配置。新生代中采用复制回收算法,大部分对象分配在Eden区,都是朝生夕死的,如果gc回收发现可达对象,被复制到Survivor From区,同时gc年林+1,如果下次还可达,会复制到Survivor To区,同时gc 年龄+1,gc年龄记录在对象头中。
当年龄比较大的时候,默认为15,会进入老年代。
大对象直接分配在老年代(比如Eden区无法放下,Survivor区也放不下)
新生代触发的gc是minor GC,老年代触发的gc是Full gc
新生代中的垃圾回收器都是采用复制算法,如Parallel Scavenge,老年代基本使用标记整理算法,如Parallel Old采用标记整理算法,CMS采用标记清除算法,并发收集,响应时间优先。
GC在回收的时候,会发生Stop The World,暂停用户线程,频繁的GC回收,可能导致卡顿现场