java相较于c、c++语言的优点之一是自带垃圾回收器,程序开发人员不用手动管理内存,内存的分配和释放彻底由gc(Garbage Collector)来作,极大地提升了软件开发效率及程序健壮性(手动管理内存容易形成内存泄漏)。凡事皆有两面性,java gc在给咱们带来内存管理便捷性的同时,也面临STW(Stop The World)影响程序吞吐的缺陷。做为java开发人员,只有深刻理解jvm垃圾回收的机制,才能在程序性能出现瓶颈时,更好的对程序进行优化。java
欢迎学Java和大数据的朋友们加入java架构交流: 855835163
群内提供免费的架构资料还有:Java工程化、高性能及分布式、高性能、深刻浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的免费直播讲解 能够进来一块儿学习交流哦c++
在垃圾回收以前,jvm须要肯定哪些对象已死,即须要当作垃圾被回收。垃圾确认的方法传统的有引用计数法:用一个引用计数器来标记对象当前的引用次数,当引用计数为0时,对象可回收。这种方法有个弊端是没法解决循环引用的问题,如两个对象相互引用则它们永远不会释放。另一种方法是可达性分析算法,目前主流的语言(java、c#、golang等)都是采用这种方法来断定一个对象是否存活。可达性分析算法的思路是:将一系列根对象做为起点,沿着这些起点向下搜索,搜索通过的全部路径成为引用链,没有在引用链中的对象则为不可达对象,这类对象会被判断为可回收对象。可达性分析图以下:golang
可达性分析算法
标记清除算法是最基础的收集算法,顾名思义,算法分为两个阶段:首先标记须要回收的对象,而后统一回收全部被标记的对象。这种垃圾回收算法比较简单,可是存在两个不足之处:一个是效率问题,标记和清除都须要扫描整个内存空间的对象,效率较低;另外一个是标记清除后会产生大量的内存碎片。标记-清除算法流程以下所示:c#
标记-清除算法架构
为了解决上面的效率和内存碎片问题,有人提出了“复制”算法,它的思路是将内存一分为二,每次分配对象时只使用其中一半内存空间,当一块内存空间用完时,就将该块内存空间上存活的对象复制到另一块内存上,而后将已使用过的内存空间一次清理掉,这样每次只对一半的内存进行回收,同时也解决了内存碎片的问题。一样,复制算法也有很明显的缺点:1. 牺牲了一半的内存空间,内存利用率最大也就50%;2. 当内存中存活的对象较多时,会进行大量的复制操做,效率会较低。复制算法的执行过程以下所示:jvm
复制算法分布式
复制算法在存在大量存活对象的状况下效率低下,更关键的是50%的内存空间得不到利用,若是存活对象过多,内存空间可能不够,则须要额外的空间做为担保,所以,对于老年代不适合使用这种算法(新生代有老年代作担保)。源码分析
所以,根据老年代的特色,有人提出了“标记-整理”算法,标记-整理算法和标记-清除算法的第一步相同,即对全部须要回收的对象进行标记,不一样的是,标记-整理算法在第二阶段并非直接对可回收的对象进行清理,而是将全部存活的对象向一端移动,而后一把清理掉存活边界以外的内存。这种算法结合了标记-清除和复制算法的思想,即未浪费内存也避免了内存碎片的产生。“标记-整理”算法以下所示:性能
标记-整理算法
分代收集并无什么创新之处,这种算法只是将jvm堆内存分为不一样的区域,再根据区域的特色采用不一样的垃圾回收算法。通常分为新生代和老年代,新生代在再细分为Eden和Survivor区(8:1),在新生代中,对象的存活率低,所以采用“复制”算法,将Edian区存活的对象复制到Survivor区,而老年代中对象存活率高且没有额外的中间作担保,所以采用“标记-清除”或者“标记-整理”算法来进行垃圾回收。
欢迎学Java和大数据的朋友们加入java架构交流: 855835163 群内提供免费的架构资料还有:Java工程化、高性能及分布式、高性能、深刻浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的免费直播讲解 能够进来一块儿学习交流哦