[JVM] 垃圾回收机制,jvm垃圾回收机制
[JVM] 垃圾回收机制,jvm垃圾回收机制
垃圾回收机制
JAVA与C++的一个区别在于Java是基于JAVA虚拟机的,JAVA虚拟机会帮我们自动回收内存,那么JAVA虚拟机是怎么实现垃圾回收的呢。
垃圾对象的判断
要实现垃圾的自动回收,首先要定位什么哪些是垃圾,也就是不再使用的对象。这个有两种实现方法:
- 引用计数算法:对象每被引用一次则计数加1,引用失效则减1,当引用为0时则将其回收即可。此法实现简单,但是存在一个问题是循环引用,如A引用B,而B又引用A,此时没有其它对象在使用它们,此时A,B对象都应该被回收的,而A,B引用次数都不为0导致没法回收。
- 可达性分析算法:通过一系列被称为“GC Roots” 的对象作为起始节点,从这些对象的引用其它对象一路搜索,搜索走过的路径成为引用链。在这引用链的对象是还在使用的,不在引用链的对象则可以回收。
GC Roots有如下几种:
垃圾回收的算法
标记-清理算法:将被标记为垃圾的对象清理掉,释放其对应的空间。该方法实现简单,但是垃圾回收之后可用的内存空间不连续,存在着大量的内存碎片,不便于下一次的申请。
标记-整理算法:将被标记为垃圾的对象清理掉,释放其对应的空间,并将将活着的对象移动使得可用的内存空间连续。此法缺点在于移动或者的对象有一定的时间代价
复制算法:将内存空间平均分为两个区域,每次垃圾回收之后,将存活的对象从当前区域复制到另一区域。此法缺点在于内存空间将被折半使用,且如果存活的对象多则会复制耗时也多
以上三种方法实现上都有其不足之处,对此,虚拟机结合以上的算法将内存空间分为多个区域,并对几个区域进行特性采用不同的算法。
上面的所说的几个区域有:年轻代,年老代。其中年轻代还可以细分为eden,survivor (包括from,to)
基本思路如下:新对象一般分配在年轻代的eden区域,而新生代对象有那么一个特性就是朝生夕死,存活率很低,可以采用复制算法。当被eden回收时将活着的对象复制到survivor的from区域,而from区域被回收时则被复制到to区域,活着的对象在这两个区域反复移动直到移动次数到一定的次数后,也就是到达到一定的年龄后就被移交到年老代。此时在年老代的对象是较为稳定的,不易被回收的,因为可采用标记-清理算法或者标记-整理算法进行回收。
垃圾回收器
基于上面的所说的垃圾回收算法,JAVA虚拟机实现了多种的垃圾回收器。
首先,我们先从宏观上看下这几种垃圾回收器,它们主要根据两个维度来划分,一个维度是年轻代/年老代,另一个维度是单线程/多线程。
Serial收集器
这是一个单线程的年轻代收集器,它主要适用于单核的情况,因为单线程可以减少线程切换时的开销。是Client模式下的默认新生代收集器,它的一个特点就是工作时会发生“Stop The World”–停止所有其他工作线程,直到它工作完成。
ParNew收集器
这就是Serial收集器的多线程版本,垃圾收集器工作时采用多线程收集,是Server模式下默认的新生代收集器,工作时同样是发生“Stop The World”,此收集器适用于多核的情况,对于单核因为存在线程切换的开销它并不会比Serial收集器效率更高。
Parallel Scavenge收集器
这个跟ParNew收集器类似,也是新生代的多线程收集器,但是这个的收集器的关注点在于达到一个可控的吞吐量,吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),如虚拟机运行用户代码耗时为99秒,用了1秒去收集垃圾,则吞吐量为99%。
Serial Old收集器
老年代单线程垃圾收集器,也就是Serial收集器的老年代版本,使用的是标记-整理算法。
Parallel Old收集器
老年代多线程垃圾收集器,也就是Parallel Scavenge收集器的老年代版本,使用的是标记-整理算法。
CMS收集器
老年代多线程垃圾收集器,是并发的收集器,而前面我们所说的是并行收集器,注意两者的区别。它关注点在于尽可能减少每次GC的停顿时间,提升响应速度,提升用户体验,如Intellij Idea默认使用的就是CMS垃圾收集器。它采用的是标记-清除算法
基本的工作步骤如下:
其中,初始标记、重新标记这两个步骤仍需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快;并发标记阶段就是进行GC Roots Tracing的过程;而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间按一般会比初始标记阶段稍长一些,但远比并发标记的时间短;并发清理则是一边清理前面标记的垃圾,一边执运行用户代码。
因为工作最长的两个阶段并发标记和并发清理都是跟用户代码一起运行的,故可以认为是一款并发的收集器。
总体上是一款很优秀的收集器,但是也有自己的不足:
G1收集器
这个收集器一个最大的特点在于,相对于前面的每个垃圾收集器只能作用于年轻代或者是年老代,它可以同时作用年轻代和年老代,它弱化了年轻代和年老代的明显的内存分代区域,将整个内存空间分为多个Region空间块。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region(这也就是Garbage-First名称的来由)
参考资料:
http://jucongyuan.github.io/2016/09/12/Java%E5%86%85%E5%AD%98%E5%9B%9E%E6%94%B6%E7%AE%80%E4%BB%8B/ (图画得很棒)
相关文章
- 暂无相关文章
用户点评