垃圾收集 Garbage Collection 一般被称为“GC”, 在jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出作入栈和出栈操做,实现了自动的内存清理,所以,咱们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部份内存的分配和使用都是动态的!java
既然是垃圾回收,那确定要知道什么是垃圾了是吧,我相信哪一天落魄java开发宜春小哥哥去捡垃圾维持生活,哈气、一juo、弯腰、开盖、倒水,一鼓作气我闭着眼,把广东靓仔没喝完的快乐肥宅水错当垃圾捡了,这广东靓仔还不得打人?是的,而JVM的垃圾收集的前提就是明确啥是垃圾!JVM首先会进行一系列计数算法判断是否是垃圾。在JVM认为是不是垃圾的评判标准为对象是否存活,对象存活就不是垃圾,不能进行回收,反之进行回收。算法
判断对象是否存活的JVM两种计数算法主要有两种,分别是:引用计数、可达性分析计数算法。编程
一听到计数算法中有“ 算法 ”二字,小白童鞋发愣了:妈呀,数学很差,完了,不学了...咳咳咳,咋们不提算法,从咋们的平常生活开始着手,在咱们平时一个东西常常没被使用,并且也用不上什么地方,那么这个东西能够说就是垃圾。其实在 Java 中也是如此,若是一个对象不可能再被引用,那么这个对象就是垃圾,应该被回收。就是这么简单粗暴,JVM两种计数算法就是这样子的,真的不复杂。设计模式
根据咱们生活上的思想,咱们很容易想到使用引用计数的方法来判断垃圾。在一个对象被引用时加一,被去除引用时减一,这样咱们就能够经过判断引用计数是否为0来判断一个对象是否为垃圾。这种方法咱们通常称之为「引用计数法」。确实贼简单的一个思路也正是觉得它的简单,从而存在缺陷,所以引用计数算法是这样定义的并发
一、引用计数:每一个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时能够回收。 二、正因方法简单,从而引用计数算法没法解决对象相互循环引用的问题。jvm
小白童鞋可能不知道啥是对象循环引用,熊dei看图 学习
首先声明一点:在实际开发语言好比java、C#等都是采用可达性分析计数算法判断对象是否存活!优化
可达性分析算法的定义以下:线程
可达性分析(Reachability Analysis):从
GC Roots
开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证实此对象是不可用的。不可达对象。设计
相信很容易看出,其实都很好理解,惟独GC Roots有点抽象,那啥是GC Roots呢?在Java语言中,GC Roots主要包括:
一、对象的引用,位于虚拟机栈中。 二、方法区中的静态引用。 三、本地方法栈中JNI的引用。【JNI通常指的是Native方法】 简单地说,GC Root 就是通过精心挑选的一些引用
以上内容概述了啥垃圾,接下来就要讲讲如何进行回收垃圾!在生活中咱们收集垃圾,能够一边走一边扫,也能够把垃圾全都扫到一块儿再一次清理。在JVM中,相似生活,提供了一系列的垃圾收集算法。
并且咱们还常常能看到垃圾分类,分红可回收垃圾,和不可回收垃圾,可是从某一意义上来说,还都是垃圾,若是人人都遵照垃圾分类规则,我估计之后收破烂的大叔大妈都每天从可回收垃圾桶下手了,其实在JVM中也相似如此,JVM采起了分区、分代收集的思想。
常见的垃圾回算法:标记清除算法、复制算法、标记整理算法、分代收集算法。
标记清除算法。从名字能够看到其分为两个阶段:标记阶段和清除阶段。一种可行的实现方式是,在标记阶段,标记全部由 GC Root 触发的可达对象。此时,全部未被标记的对象就是垃圾对象。以后在清除阶段,清除全部未被标记的对象。标记清除算法最大的问题就是空间碎片问题。若是空间碎片过多,则会致使内存空间的不连续。虽然说大对象也能够分配在不连续的空间中,可是效率要低于连续的内存空间。
复制(Copying)算法。复制算法的核心思想是将原有的内存空间划分为大小相等的两块,每次只使用一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中。以后清除正在使用的内存块中的全部对象,以后交换两个内存块的角色,完成垃圾回收。复制算法的缺点是要将内存空间折半,极大地浪费了一半内存空间。所以如今的商业虚拟机都使用这种复制算法来进行新生代的垃圾收集!由于在对象存活率高的时候,复制算法就显得效率低下
关于标记-整理算法还有一种叫法是标记压缩算法,知道其说的同一者就行了。
标记整理算法。标记整理算法能够说是标记清除算法的优化版,其一样须要经历两个阶段,分别是:标记阶段、整理阶段。在标记阶段,从 GC Root 引用集合触发去标记全部对象,和标记清除算法中的标记阶段是同样同样的。在整理阶段,其则是将全部存活的对象整理在内存的一边,以后清理边界外的全部空间。所以,标记-整理算法(Mark-Compact)不会产生内存碎片,可是会多花点时间用在整理(Compact)上面!
“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就能够根据各个年代的特色采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少许存活,那新生代选用复制算法,只须要付出少许存活对象的复制成本就能够完成收集。而老年代中由于对象存活率高、没有额外空间对它进行分配担保,老年代就使用“标记-清理”或“标记-整理”算法来进行回收。
标记清除算法
分为标记阶段和清除两阶段。标记清除算法最大的问题就是空间碎片问题。比较适合在存活对象比较多的状况。
复制算法(Copying)
复制算法的缺点是浪费了一半内存空间。如今的商业虚拟机都使用这种复制算法来进行新生代的垃圾收集!由于在对象存活率高的时候,复制算法就显得效率低下比较适合存活对象比较少的状况。
标记整理算法(Mark-Compact)
标记整理算法是标记清除算法的优化版,标记-整理算法(Mark-Compact)不会产生内存碎片,可是会多花点时间用在整理(Compact)上面!
分代收集算法(Generational Collection)
新生代选用复制算法,老年代使用“标记-清理”或“标记-整理”算法来进行回收。
借用大佬整理的一张图:
若是本文对你有一点点帮助,那么请点个赞呗,谢谢~
最后,如有不足或者不正之处,欢迎指正批评,感激涕零!若是有疑问欢迎留言,绝对第一时间回复!
欢迎各位关注个人公众号,里面有一些java学习资料和一大波java电子书籍,好比说周志明老师的深刻java虚拟机、java编程思想、核心技术卷、大话设计模式、java并发编程实战.....都是java的圣经,不说了快上Tomcat车,咋们走!最主要的是一块儿探讨技术,向往技术,追求技术,说好了来了就是盆友喔...