GC的主要区域
GC主要回收的是堆中数据
GC判断是否为垃圾对象的俩种方法
- 引用计数法
- 可达性算法
引用计数法
是在创建对象后,每添加一个对象的引用,该对象的引用计数器就会加1,如该对象为java方法中的本地对象则在方法调用后会销毁该对象,计数器并减1;直到为0时则会触发垃圾回收
问题?
该垃圾回收机制在遇到循环引用的问题时,则会出现永久不会回收的情况
可达性算法
从GC root起,到堆中每一个相关对象判断是否有引用链可达,若可达则标记为正确,若不可达则判定为垃圾对象,则执行垃圾回收
可以作为GC root的对象
- 虚拟机栈中引入的对象
- 元数据区类中常量引用的对象
- 元数据区类中静态变量引用的对象
- 本地方法栈中JNI(Native)方法引用的对象
- 活跃线程的引用对象
垃圾回收算法
- 标记/清除算法
- 复制算法
- 标记整理算法
- 分代收集算法
标记/清除算法
标记清除算法,借用可达性算法,将不可达对象进行标记,并清除,总共分为俩个阶段:标记阶段与回收阶段
缺点:容易出现碎片化空间
复制算法
开辟俩片空间,大小相等,将对象默认创建到其中一个区中,当触发垃圾回收时,将存活对象拷贝到另一空间,将当前对象空间进行清理
缺点:空间利用率减少一半
标记整理算法
其为对标记清除算法的优化,在标记之后对数据进行整理,减少碎片化
分代收集算法
将堆内存分为不同代,使用不用的回收算法进行回收对象
1.7之前划分年轻代、老年代、永久代,1.7之后废除永久代,引入元数据空间
年轻代:使用复制算法
老年代:数据存活量大,使用标记-清除算法/标记-整理算法
年轻代对象如何进入老年代?
- survivor区存不下存活对象
- 对次年轻代收集后仍存活的对象
- 新建了一个较大的对象
- 涉及到相关的jvm调优参数有
-XX:SurvivorRaion: Eden区与Survivor区的比值,默认为8:1
-XX:NewRation: 老年代与年轻代的比值,默认为2:1
-XX:MaxTenuringThreshold:对象从年轻代到老年代经过GC次数最大的阀值
引发FULL GC的条件
- 老年代空间不足
- 程序调用System.gc()
垃圾回收关系密切的几个关键字
- Stop-The-World:解释如下
- 执行GC时,应用程序终止运行
- 任何一种GC算法中都会发生
- 多数GC优化都是通过减少Stop-The-World的时间来提高程序性能
- SafePoint:解释如下
- 分析过程中对象引用关系不会发生变化的点
- 产生SafePoint的地方:方法调用、循环跳转、异常跳转等
- 安全点数量适中
常用的垃圾回收器
- 年轻代常见的垃圾回收器有
- Serial收集器(-XX:+UseSerialGC,复制算法)
- 单线程收集,在垃圾收集时,会暂停所有工作线程
- 简单高效,Client模式下默认的年轻代收集器
- ParNew收集器(-XX:+UseParNewGC,复制算法)
- 多线程收集,在垃圾回收时,暂停所有工作线程
- 单核执行不如Serial,多核有优势
- Parallel Scavenge收集器(-XX:+UseParallelGC,复制算法)
- 比起关注用户线程停顿时间,更关注系统的吞吐量
- 多核下才有优势,Server模式下年轻代默认的垃圾回收器
- Serial收集器(-XX:+UseSerialGC,复制算法)
- 老年代常见的垃圾回收器有
- Serial OId收集器(-XX:+UseSerialOldGC,标记-整理算法)
- 单线程收集,在垃圾收集时,会暂停所有工作线程
- 简单高效,Client模式下默认的老年代收集器
- CMS收集器(-XX:+UseConcMarkSweepGC,标记-清除算法)
- 初始标记:stop-the-world
- 并发标记:并发追溯标记,程序不会停顿
- 并发预清理:查找执行并发标记阶段从年轻代晋升到老年代的对象
- 重新标记:暂停虚拟机,扫描CMS堆中的剩余对象
- 并发清理:清理垃圾对象,程序不会卡顿
- 并发重置:重置CMS收集器的数据结构
- Parallel Old收集器(-XX:+UseParallelOldGC,标记-整理算法)
- 多线程,更关注系统的吞吐量
- Serial OId收集器(-XX:+UseSerialOldGC,标记-整理算法)
- G1垃圾收集器(-XX:+UseG1GC,复制+标记-整理算法)
- 并行和并发,垃圾收集线程与工作线程并发执行
- 分代收集
- 空间整合
- 可预测的停顿
垃圾回收的几个问题点
- finalize()方法
- 强引用、软引用、弱引用、虚引用的区别
强引用就是一般创建对象的方式,new方法创建,一般即使内存不够用,也不会主动销毁
//ArrayList中如何清理对象,相当于将所有对象重为空,等待GC回收
/**
* Removes all of the elements from this list. The list will
* be empty after this call returns.
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
软引用:软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示,当内存不够时,会被GC清理
String str=new String("abc"); // 强引用
SoftReference<String> softRef=new SoftReference<String>(str); // 软引用x
softRef.get() //得到str对象,如果str被回收,则返回null
//当内存不足时
If(JVM.内存不足()) {
str = null; // 转换为软引用
System.gc(); // 垃圾回收器进行回收
}
弱引用:如果一个对象只有弱引用,那么就类似可有可无的生活用品,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。
弱引用与软引用的区别是无论内存是否充足,GC都会处理弱引用对象
WeakReference<String> sr = new WeakReference<String>(new String("hello"));
System.out.println(sr.get());
System.gc(); //手工模拟JVM的gc进行垃圾回收
System.out.println(sr.get());
虚引用:“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。若一个对象只有虚引用,那则可能在任何时刻被JVMGC回收,虚引用与软引用或者弱引用的区别是,其需要依赖于引用队列,当一个对象被GC回收时,判断到其仅有虚引用,则会将这个虚引用加入引用队列,故虚引用一般用于跟踪对象垃圾回收过程的。
Object obj = new Object();
ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
PhantomReference<Object> pf = new PhantomReference<Object>(obj,rq);
obj=null;
System.out.println(pf.get());//永远返回null
System.out.println(pf.isEnqueued());//返回是否从内存中已经删除
System.gc();
TimeUnit.SECONDS.sleep(6);
System.out.println(pf.isEnqueued());