2.根搜索方法 根搜索方法是通过一些“GC Roots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链(Reference Chain), 当一个对象没有被GC Roots的引用链连接的时候,说明这个对象是不可用的。 GC Roots对象包括: a) 虚拟机栈(栈帧中的本地变量表)中的引用的对象。 b) 方法区域中的类静态属性引用的对象。 c) 方法区域中常量引用的对象。 d) 本地方法栈中JNI(Native方法)的引用的对象。 了解了JVM是怎么确定对象是“垃圾”之后,进入正题,让我们来看看垃圾回收的算法。
1.复制算法(Copying) 复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉。 复制算法实现简单,运行效率高,但是由于每次只能使用其中的一半,造成内存的利用率不高。 现在的JVM用复制方法收集新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所以两块内存默认大概是8:1。 垃圾回收前:
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EYzVMekUwTnpnM05ETXdOak01TURFeE1EQT0%3D.jpg?w=700&webp=1)
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EZ3dMekUwTnpnM05ETXdOalF3TlRJNE9UZz0%3D.jpg?w=700&webp=1)
2.标记—清除算法(Mark-Sweep) 标记—清除算法包括两个阶段:“标记”和“清除”。 在标记阶段,确定所有要回收的对象,并做标记。 清除阶段,将标记阶段确定不可用的对象清除。 标记—清除算法是基础的收集算法,标记和清除阶段的效率不高,而且清除后回产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间。 垃圾回收前:
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EYzNMekUwTnpnM05ETXdOalF3TWpVNU16VT0%3D.jpg?w=700&webp=1)
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EYzRMekUwTnpnM05ETXdOak0zTnpneU5EZz0%3D.jpg?w=700&webp=1)
3.标记—整理算法(Mark-Compact) 标记—整理算法和标记—清除算法一样,但是标记—整理算法不是把存活对象复制到另一块内存,而是把存活对象往内存的一端移动,然后直接回收边界以外的内存。 标记—整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代。 垃圾回收前:
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EYzVMekUwTnpnM05ETXdOak01TURFeE1EQT0%3D.jpg?w=700&webp=1)
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EZ3dMekUwTnpnM05ETXdOalF3TlRJNE9UZz0%3D.jpg?w=700&webp=1)
二. JVM垃圾收集器 垃圾收集器就是收集算法的具体实现,不同的虚拟机会提供不同的垃圾收集器。并且提供参数供用户根据自己的应用特点和要求组合各个年代所使用的收集器。 本文讨论的收集器基于Sun Hotspot虚拟机1.6版。 下图中展示了jdk1.6中提供的6种作用于不同年代的收集器,两个收集器之间存在连线的话就说明它们可以搭配使用。没有最好的收集器,也没有万能的收集器,只有最合适的收集器。 从Serial收集器到Parallel收集器,再到CMS收集器, G1收集器,用户线程的停顿时间在不断缩短,但是仍然没有办法完全消除。
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1ERXpMMWRGUWxKRlUwOVZVa05GWm1FM016UTNPRFF4TnpsbVlqTm1aakU0TVRFeVpXUXpaRGMwT1RnME0yWT0%3D.jpg?w=700&webp=1)
新生代Serial与年老代Serial Old搭配垃圾收集过程图:
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EZ3hMekUwTnpnM05ETXdOalF3T1RFd01URT0%3D.jpg?w=700&webp=1)
3. Parallel Scavenge收集器 同ParNew一样是使用复制算法的新生代并行多线程收集器。 Parallel Scavenge的特点是它的关注点与其他收集器不同, CMS等收集器的关注点尽可能地缩短垃圾收集时用户线程的停顿时间, 而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量,也被称为吞吐量优先收集器。 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。 高吞吐量和停顿时间短的策略相比,主要强调任务更快完成,适用于后台运算而不需要太多交互的任务;而后者强调用户交互体验。
Parallel Scavenge提供两个参数精确控制吞吐量, -XX:MaxGCPauseMillis控制最大垃圾收集停顿时间和-XX:GCTimeRatio设置吞吐量大小 1).MaxGCPauseMillis允许的值是一个大于零0的毫秒数,收集器将尽力保证内存回收花费的时间不超过设定值。 GC停顿时间缩小是以牺牲吞吐量和新生代空间来换取的,也就是要使停顿时间更短,需要使新生代的空间减小,这样垃圾回收的频率会增加,吞吐量也降下来了。 2).GCTimeRatio的值是一个大于0小于100的整数,也就是垃圾收集时间占总时间的比率。默认为99,则允许最大GC时间就占总时间的1/(1+99). 3).-XX:+UseAdaptiveSizePolicy,打开GC自适应调节策略后会自动设置新生代大小、调整Eden与Survior区的比例、晋升老年代对象年龄,新生代大小等细节参数。这个参数也是Parallel Scavenge和ParNew的重要区别。
算法:复制算法 应用:适合在后台运算而不需要太多交互的任务
新生代ParNew/Parallel Scavenge与年老代Serial Old搭配垃圾收集过程图:
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EZ3lMekUwTnpnM05ETXdOalF4TXpNMk9EVT0%3D.jpg?w=700&webp=1)
4. Serial Old收集器 是Serial收集器的老年代版本,也同样是一个单线程的收集器,使用标记-整理算法。主要是client模式下的虚拟机使用。参考上面图Serial/Serial old. 两大用途: (1) 在JDK1.5及之前的版本中与Parallel Scavenge搭配使用; (2) 作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。 收集过程:暂停所有用户线程, 单线程 算法:标记-整理算法 应用:主要意义是Client模式下的收集器,如果在Server模式下:参看上面的两大用途。
5. Parallel Old收集器 是Parallel Scavenge收集器的老年代版本, 在JDK1.6中才开始使用。 由于之前的版本中,Parallel Scavenge只有使用Serial Old作为老年代收集器,其吞吐量优先的设计思路不能被很好的贯彻. 在Parallel Old收集器出现后,Parallel Scavenge和Parallel Old的配合主要用于贯彻这种吞吐量优先的设计思路。 收集过程:多线程 算法:标记-整理算法 应用:在注重吞吐量及CPU资源敏感的场合,可以优先考虑Parallel Scavenge加Parallel Old收集器
新生代Parallel Scavenge和年老代Parallel Old收集器搭配运行过程图:
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EZ3pMekUwTnpnM05ETXdOak00TmpnMk1EZz0%3D.jpg?w=700&webp=1)
6. CMS收集器 Concurrent Mark Sweep 以获取最短回收停顿时间为目标的收集器,比较理想的应用场景是B/S架构的服务器。 CMS收集器工作过程:
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EZzBMekUwTnpnM05ETXdOak01T1RJeU1qaz0%3D.jpg?w=700&webp=1)
7. G1收集器 (jdk1.7后全新的回收器, 用于取代CMS) HotSpot开发团队赋予它的使命是未来可以替换掉JDK1.5中发布的CMS收集器。其与其它收集器相比,G1具备如下特点:
- 并行与并发:和CMS类似。
- 分代收集:分代概念在G1中依然得以保留。虽然G1可以不需要其它收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。也就是说G1可以自己管理新生代和老年代了。
- 空间整合:由于G1使用了独立区域(Region)概念,G1从整体来看是基于“标记-整理”算法实现收集,从局部(两个Region)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片。
- 可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用这明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
- 初始标记(Initial Making)
- 并发标记(Concurrent Marking)
- 最终标记(Final Marking)
- 筛选回收(Live Data Counting and Evacuation)
![JVM垃圾回收算法和收集器 JVM垃圾回收算法和收集器](https://image.shishitao.com:8440/aHR0cHM6Ly93d3cuaXRkYWFuLmNvbS9nby9hSFIwY0RvdkwyNXZkR1V1ZVc5MVpHRnZMbU52YlM5NWQzTXZjbVZ6THpFeE1EZzFMekUwTnpnM05ETXdOak0yT1Rrek5qRT0%3D.jpg?w=700&webp=1)
而G1的各代存储地址是不连续的,每一代都使用了n个不连续的大小相同的Region,每个Region占有一块连续的虚拟内存地址。