通常有两种方式(引用计数法、可达性分析),JVM使用的是可达性分析算法
给对象添加一个引用计数器,当对象增长一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收(Python 在用,但主流虚拟机没有使用)缓存
来断定对象是否存活的。这个算法的基本思路就是经过一系列的称为“GC Roots”的对象做为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时,则证实此对象是不可用的bash
做为 GC Roots 的对象包括下面几种:服务器
Java提供finalize()方法,垃圾回收器准备释放内存的时候,会先调用finalize(),能够完成对象的拯救(不被回收),可是不能保证必定不被回收,说白了就是没啥用,一个坑多线程
Reference 中存储的数据表明的是另外一块内存的起始地址并发
通常的 Object obj = new Object() ,就属于强引用。 (若是有 GCroots 的强引用)垃圾回收器绝对不会回收它,当内存不足时宁愿抛出 OOM 错误,使得程序异常中止,也不会回收强引用对象jvm
SoftReference垃圾回收器在内存充足的时候不会回收它,而在内存不足时会回收它网站
示例代码:spa
public static void main(String[] args) {
String str = new String("SunnyBear"); // 强引用
SoftReference<String> strSoft = new SoftReference<String>(str);
str = null; // 干掉强引用,确保只有strSoft的软引用
System.out.println(strSoft.get()); // SunnyBear
System.gc(); // 执行一次gc,此命令请勿在线上使用,仅做示例操做
System.out.println("------------ gc after");
System.out.println(str); // null
System.out.println(strSoft.get()); // SunnyBear
}
复制代码
因此软引用通常用来实现一些内存敏感的缓存,只要内存空间足够,对象就会保持不被回收掉线程
垃圾回收器在扫描到该对象时,不管内存充足与否,都会回收该对象的内存
示例代码:
public static void main(String[] args) {
String str = new String("SunnyBear"); // 强引用
WeakReference<String> strWeak = new WeakReference<String>(str);
str = null; // 干掉强引用,确保只有strSoft的软引用
System.out.println(strWeak.get()); // SunnyBear
System.gc(); // 执行一次gc,此命令请勿在线上使用,仅做示例操做
System.out.println("------------ gc after"); // null
System.out.println(str); // null
System.out.println(strWeak.get()); // null
}
复制代码
实际应用,如WeakHashMap、ThreadLocal
幽灵引用,最弱,被垃圾回收的时候收到一个通知,若是一个对象只具备虚引用,那么它和没有任何引用同样,任什么时候候均可能被回收
虚引用主要用来跟踪对象被垃圾回收器回收的活动
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另一块上面,而后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂状况,只要按顺序分配内存便可, 实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半。
注: 专门研究代表,新生代中的对象 90%是“朝生夕死”的,因此通常来讲回收占据 10% 的空间够用了,因此并不须要按照 1:1 的比例来划份内存空间,而是 将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor[1]。当回收时,将 Eden 和 Survivor 中还存活着的对象一 次性地复制到另一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。 HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8:1,也就是每次新生代中可用内存空间为整个新生代容量的 90%(80%+10%),只有 10%的内存会被 “浪费”。
首先标记全部须要回收的对象,而后统一回收被标记的对象
首先标记出全部须要回收的对象,在标记完成后,后续步骤不是直接对可回收对象进行清理,而是让全部存活的对象都向一端移动,而后直接清理掉端,边界之外的内存
jvm 垃圾回收器把上面的三种算法所有用到了,采用分代收集
收集器 | 收集对象和算法 | 收集器类型 |
---|---|---|
Serial | 新生代,复制算法 | 单线程 |
ParNew | 新生代,复制算法 | 并行的多线程收集器 |
Parallel Scavenge | 新生代,复制算法 | 并行的多线程收集器 |
收集器 | 收集对象和算法 | 收集器类型 |
---|---|---|
Serial Old | 老年代,标记整理算法 | 单线程 |
Parallel Old | 老年代,标记整理算法 | 并行的多线程收集器 |
CMS(Conc Mark Sweep ) | 老年代,标记清除算法 | 并行和并发收集器 |
G1(Garbage First) | 跨新生代和老年代,复制算法 + 标记整理算法 | 并行和并发收集器 |
注:
jps -v
能够看到使用的垃圾收集器,例如:-XX:+UseConcMarkSweepGC
(CMS)连线表示能够 新生代 和 老年代 配套使用的垃圾收集器
最古老的,单线程,独占式,成熟,适合单 CPU 服务器 -XX:+UseSerialGC 新生代和老年代都用串行收集器
ParNew 和 Serial 基本没区别,惟一的区别:多线程,多 CPU 的,停顿时间比 Serial 少
-XX:+UseParNewGC 新生代使用 ParNew,老年代使用 Serial Old
能够和CMS搭配使用
关注吞吐量的垃圾收集器,高吞吐量则能够高效率地利用 CPU 时间,尽快完成程序的运算任务,主要适合在后台运算而不须要太多交互的任务。所谓吞吐量就是 CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了 100 分钟,其中垃圾收集花掉 1 分钟,那有吞吐效率就是 99%
收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的 Java 应用集中在互联网站或者 B/S 系统的服务端上,这类应用尤为重视服务的响应速度,但愿系统停顿时间最短,以给用户带来较好的体验。
CMS 收集器就很是符合这类应用的需求。-XX:+UseConcMarkSweepGC
,通常新生代使用 ParNew,老年代的用 CMS,从名字(包含“Mark Sweep”)上就能够看出,CMS 收集器是基于“标记—清除”算法实现的,它的运做过程相对于前面几种收集器来讲更复杂一些
整个过程分为 4 个步骤,包括:
G1相比较CMS的改进
可预测的停顿:
G1 收集器之因此能创建可预测的停顿时间模型,是由于它能够有计划地避免在整个 Java 堆中进行全区域的垃圾收集。G1 跟踪各个 Region 里面的垃圾堆积的价值大小(回收所得到的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据容许的收集时间,优先回收价值最大的 Region(这也就是 Garbage-First 名称的来由)。这种使用 Region 划份内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限的时间内能够获取尽可高的收集效率
G1 把堆划分红多个大小相等的 独立区域(Region),新生代和老年代再也不物理隔离
G1 算法将堆划分为若干个独立区域(Region),它仍然属于分代收集器。不过,这些区域的一部分包含新生代,新生代的垃圾收集依然采用暂停全部应用线程的方式,将存活对象拷贝到老年代或者 Survivor 空间。例如其中一个独立区域如图:
1. Young GC
Young GC 主要是对 Eden 区进行 GC,它在 Eden 空间耗尽时会被触发。在这种状况下,Eden 空间的数据移动到 Survivor 空间中,若是 Survivor 空间不够,Eden 空间的部分数据会直接晋升到老年代空间。Survivor 区的数据移动到新的 Survivor 区中,也有部分数据晋升到老年代空间中。最终 Eden 空间的数据为空,GC 中止工做,应用线程继续执行
2. Mixed GC
选定全部新生代里的 Region,外加根据 global concurrent marking 统计得出收集收益高的若干老年代 Region。在用户指定的开销目标范围内尽量选择益高的老年代 Region。Mixed GC 不是 full GC,它只能回收部分老年代的 Region。若是 mixed GC 实在没法跟上程序分配内存的速度,致使老年代填满没法继续进行 Mixed GC,就会使用 serial old GC(full GC)来收集整个 GC heap。因此咱们能够知道,G1 是不提供 full GC 的
大体分为4个步骤:
参数 | 描述 |
---|---|
UseSerialGC | 虚拟机运行在 Client 模式下的默认值,打开此开关后,使用 Serial+Serial Old 的收集器组合进行内存回收 |
UseParNewGC | 打开此开关后,使用 ParNew + Serial Old 的收集器组合进行内存回收 |
UseConcMarkSweepGC | 打开此开关后,使用 ParNew + CMS + Serial Old 的收集器组合进行内存回收。Serial Old 收集器将做为 CMS 收集器出现 Concurrent Mode Failure 失败后的后备收集器使用 |
UseParallelGC | 虚拟机运行在 Server 模式下的默认值,打开此开关后,使用 Parallel Scavenge + Serial Old(PS MarkSweep) 的收集器组合进行内存回收 |
UseParallelOldGC | 打开此开关后,使用 Parallel Scavenge + Parallel Old 的收集器组合进行内存回收 |
SurvivorRatio | 新生代中 Eden 区域与 Survivor 区域的容量比值,默认为 8,表明 Eden : Survivor = 8 : 1 |
PretenureSizeThreshold | 直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配 |
MaxTenuringThreshold | 晋升到老年代的对象年龄,每一个对象在坚持过一次 Minor GC 以后,年龄就增长 1,当超过这个参数值时就进入老年代 |
UseAdaptiveSizePolicy | 动态调整 Java 堆中各个区域的大小以及进入老年代的年龄 |
HandlePromotionFailure | 是否容许分配担保失败,即老年代的剩余空间不足以应付新生代的整个 Eden 和 Survivor 区的全部对象都存活的极端状况 |
ParallelGCThreads | 设置并行 GC 时进行内存回收的线程数 |
GCTimeRatio GC | 时间占总时间的比率,默认值为 99,即容许 1% 的 GC 时间,仅在使用 Parallel Scavenge 收集器生效 |
MaxGCPauseMillis | 设置 GC 的最大停顿时间,仅在使用 Parallel Scavenge 收集器时生效 |
CMSInitiatingOccupancyFraction | 设置 CMS 收集器在老年代空间被使用多少后触发垃圾收集,默认值为 68%,仅在使用 CMS 收集器时生效 |
UseCMSCompactAtFullCollection | 设置 CMS 收集器在完成垃圾收集后是否要进行一次内存碎片整理,仅在使用 CMS 收集器时生效 |
CMSFullGCsBeforeCompaction | 设置 CMS 收集器在进行若干次垃圾收集后再启动一次内存碎片整理,仅在使用 CMS 收集器时生效 |