垃圾收集算法是内存回收的方法论;垃圾收集器是内存回收的具体实现;java
自动内存管理是: 给对象分配内存和回收废弃对象占用的内存。算法
垃圾回收的范围: java堆和方法区。数组
1) 引用计数算法(主流虚拟机并未采纳): 给对象添加一个引用计数器,每当有一个地方引用到它时,计数器加1;当引用失效时,计数器值就减1;任什么时候刻当一个对象的引用计数器为0时,它就变为废弃对象。能够被回收。安全
优势: 实现简单,判断效率高;多线程
缺点: 很难解决对象之间的相互循环引用的问题。并发
2)可达性分析法: 算法思想是经过一系列的称为“GC Roots”的对象做为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,当一个对象到"GC Roots"没有任何的引用链时,则说明此对象是不可用的。jvm
可做为"GC Roots"的对象包括如下:性能
一、虚拟机栈中引用的对象;线程
二、方法区中类静态属性引用的对象;3d
三、方法区中常量引用的对象;
四、本地方法栈中JNI(即通常native方法)引用的对象。
3)肯定生存仍是死亡
即便是在可达性分析法中不可达的对象,也不是被当即回收的。由于要真正被看成废弃对象进行回收,至少要通过两次“标记”过程。
当对象在被第一次发现与一系列“Gc Roots”节点之间没有引用链时,那么它将会被标记一次,而且进行一次筛选,筛选的条件是是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过。虚拟机将视为“没有必要执行”。
若是该对象被断定有必要执行finalize()方法,那该对象将会被防止在一个F-Queue队列中,并在稍后由一个低优先级的线程中执行。finalize()方法是对象逃过被垃圾回收的最后一次机会。稍后会对F-Queue进行第二次标记。若是期间尚未与任何对象创建关系,那在第二次标记后基本上真的要被回收了。
1)标记-清除算法
标记清除算法实现思路包含两个阶段,第一个阶段,根据可达性分析算法标记全部不可达的「垃圾」,第二阶段,直接释放这些对象所占用的内存空间。
优势:是垃圾回收的基础思想算法,后续的回收算法都是基于这种算法进行改进。
缺点:
2)复制算法
将内存划分为两份大小相等的块,每次只使用其中的一块,当系统发起 GC 收集动做时,将当前块中依然存活的对象所有复制到另外一块中,并整块的释放当前块所占内存空间。
现代商用虚拟机:都是采用这种收集算法来回收新生代区域的对象。通常是将内存分为一块较大的Eden和两块较小的Survivor空间。默认比例是8:1:1。
这样的话,内存使用率就由50%提高至90%,可是即便是这样,也不能保证每次回收的都只有很少于10%的对象存活。这个时候,就要由其余内存(主要是老年代)来进行内存分配担保。
3)标记整理算法
因为复制算法在对象存活率较高时,就必需要进行较多的复制操做,效率就会变的很低,更关键的是若是不想浪费50%的内存,就须要有额外的空间进行内存分配担保,来应对超过90&对象存活的极端状况。因此在老年代中通常是不能直接采用这种算法。
根据老年代的特色,提出的标记-整理算法、其实就是增强版的标记-清除算法。只不过是否是直接将可回收的对象直接清除。是将存活对象向同一端移动,而后回收边界之外的内存。
4)分代收集算法
当前主流的jvm虚拟机,都是采用“分代收集”算法,这种算法并非新的思想,只是根据对象存活周期的特色,将java堆分为几块。新生代和老年代。“新生代”通常是对象短期是大量废弃,采用复制算法效率较高。“老年代”通常对象存活率高没有额外的空间内存进行内存分配担保,就必须使用“标记-清理”或“标记-清除”算法了。
如图是做用于不一样分代的垃圾收集器,若是两个收集器之间存在连线,就能够搭配使用。虚拟机所在的区域,则表示它是属于新生代收集器仍是老年代收集器
“Stop the World“会在任何一种GC算法中发生。“Stop the World“意味着 JVM 由于要执行GC而中止了应用程序的执行。
Serial收集器: 单线程,新生代收集器,使用复制算法。它只会使用一个CPU或一条收集线程去完成垃圾收集工做,在进行垃圾收集时,必须“Stop the World“,暂停替他全部的工做线程,直到它收集结束。
ParNew收集器: Serial收集器的多线程版本,控制参数、收集算法、Stop the World、对象分配规则、回收策略都与Serial收集器彻底同样
Parallel Scavenge收集器: 生代收集器,使用复制算法,并行多线程。
Serial Old收集器: Serial收集器的老年代版本,单线程,使用标记-整理算法。
Parallel Old收集器: Parallel Scavenge收集器的老年代版本,多线程,使用标记-整理算法
CMS收集器: 一种以获取最短回收停顿时间为目标的收集器,基于“标记-清除”算法。运做过程分四个步骤:初始标记 、并发标记、从新标记、并发清除。
初始标记、从新标记这两个步骤仍然须要“Stop the World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。并发标记阶段就是进行GC Roots Tracing 的过程,而从新标记阶段则是为了修正并发标记期间由于用户程序继续运做而致使标记产生变更的那一部分对象的标记记录,这一阶段的停顿时间通常会比初始标记阶段稍长一些,但远比并发标记的时间段。
整个过程当中耗时最长的并发标记和并发清除过程,收集器线程均可以与用户线程一块儿工做,因此,CMS收集器的内存回收过程是与用户线程一块儿并发执行的。
优势: 并发收集,低停顿
缺点: 对CPU资源很是敏感、没法处理浮动垃圾、基于标记清除算法,收集结束时有大量控件碎片产生
G1收集器: G1收集器是当今收集器技术发展最前沿成果之一,一种面向服务端应用的垃圾收集器。
G1的特色: 并行与并发、分代手机、空间整合、可预测的停顿
运做过程以下: 初始标记、并发标记、最终标记、筛选回收。
初始标记阶段: 仅仅只是标记一下GC Roots能直接关联到的对象,而且修改TAMS的值,让下一阶段用户程序并发运行时,能在正确可用的Region中建立新对象,这阶段须要停顿线程,但耗时很短。
并发标记阶段: 是从GC Roots开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行。
而最终标记阶段: 则是则是为了修正在并发标记期间因用户程序继续运做而致使标记产生变更的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面。最终标记阶段须要把Remembered Set Logs的数据合并到Remembered Set中,这阶段须要停顿线程,可是可并行执行。
最后在筛选回收阶段: 首先对各个Region的回收价值和成本进行排序。根据用户所指望的GC停顿时间来制定回收计划。
对象优先在Eden分配: 大多数状况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。
大对象直接进入老年代: 大对象是指须要大量连续内存控件的Java对象,最典型的大对象就是那种很长的字符串以及数组。
长期存活的对象将进入老年代: 虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代。为了作到这点,虚拟机给每一个对象定义了一个对象年龄计数器。若是对象在Eden出生并通过第一次Minor GC后仍然存活,而且能被Survivor容纳的话,将被移动到Survivor空间中,而且对象年龄设为1,对象在Survivor区中每“熬过”一次Minor GC,年龄就增长1岁,当它的年龄增长到必定程度(默认为15岁),就将会被晋升到老年代。
动态对象年龄断定: 虚拟机并非永远要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,若是在Survivor空间中相同年龄全部对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就能够直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
空间分配担保: 在发生Minor GC以前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代全部对象总空间,若是这个条件成立,那么Minor GC能够确保是安全的。若是不成立,则虚拟机会查看HandlePromotionFailure设置值是否容许担保失败。
内存回收与垃圾收集器在不少时候都是影响系统性能、并发能力的主要因素之一,虚拟机之因此提供多种不一样的收集器以及提供大量的调节参数,是由于只有根据实际应用需求、实现方式选择最优的收集方式才能获取最高的性能。没有固定收集器、参数组合,也没有最优的调优方法,虚拟机也就没有什么必然的内存回收行为。