1.什么是垃圾回收
垃圾回收主要说的是java会自动把程序在运行过程中产生的一些没有用的对象给回收掉,这样可以避免内存的浪费。
java主要是通过一个叫“根可达”的算法来识别这个对象是否可以被回收的,然后回收的算法也主要有三种:标记清除,拷贝,标记压缩。
标记清除:在内存里面,找到可以回收的对象,然后直接删除,但是这样会导致内存碎片化。
拷贝:就是在内存使用的时候,只使用一半的内存,比如说有一块内存,分成两半,分别是A,B。一开始B内存是没有使用的,然后在A内存里面找到有用的对象,直接拷贝到b内存里面去,完事之后,直接把A内存里面的数据给删掉。这样的效率比较高,但是浪费内存空间。
标记压缩:在内存里面,先把没有用的对象给回收掉,然后重新把有用的对象进行排序,这样内存里面的对象是有序的。但是他的效率比较低。
2.GC算法
1. 在这个三种回收算法的基础上,出现了十几种垃圾回收器。
2. GC的演化过程:主要随着内存的不断增长,而陆续出现新的垃圾回收器。
3. 在java1.8里面主要是分代算法,分代算法可以说是由拷贝算法演变而来,分代算法的jvm垃圾回收器有6个,分代算法里面有新生代和老年代,新生代有伊甸园区和幸存者区,其中幸存者区有两个区域,他们之间的默认比例是8比1比1
4.首先新创建的对象会在伊甸园区,等这个区的容量满了之后,进行垃圾回收,会把存活的对象放到幸存者1区,然后直接清空伊甸园区,当第二次进行垃圾回收的时候,会把伊甸园区里面存活的对象跟幸存者1区的对象直接放到幸存者2区,并且同时清除伊甸园区跟幸存者1区,第三次垃圾回收的时候,会把伊甸园区里面存活的对象跟幸存者2区的对象直接放到1区,然后进行清空操作,在进行垃圾回收的时候会重复这个操作,然后满足一定条件的对象,会被转移到老年代里面
3.遇到的问题
以上这个是最简单的垃圾回收的流程,但是有一个问题,那就是在早期回收垃圾的时候,业务线程是会暂停的,回收垃圾的线程在运行(进行垃圾回收),也就是说,在回收垃圾的过程中,很多线程都是暂停的,那这样的话,应用的响应时间就会变慢。
举个例子:有用户在下单的时候,应用的运行内存满了,jvm进行垃圾回收,那用户下单时候就会被卡住,所以这个响应时间就会变慢(在系统里面,比较重要的两点,第一个是吞吐量,第二个是响应时间,一般我们在开发里面,进行的JVM调优一般都是调优响应时间)
而且当内存比较大的时候,一个线程进行垃圾回收会比较慢(可以理解为你一个人收拾100平米的房子,会很吃力),所以后面就演变出了多线程来进行垃圾回收,但是这个多线程也不是越多越好,因为会涉及到cpu的上下文切换,还有就是当你的电脑是四核,那你设置5个线程,那这个时候的效果就不是很好,所以就出现的了CMS回收器
4.CMS回收器跟三色算法
CMS回收器(并发垃圾回收):举个例子,早期的垃圾回收器是业务线程执行,要回收垃圾的时候,垃圾回收线程执行,业务线程暂停。那这个CMS就是垃圾回收线程一起参与你的业务,如果产生垃圾,也不用你的线程暂停,直接就把垃圾回收掉。
但是CMS垃圾回收器有一个问题,那就是回收线程在标记了某一个对象是否为垃圾之后,由于cpu的上下文切换导致了回收线程暂停了,那么回收线程重新运行之后,如何判断标记的对象的状态是否改变。因为有可能不是垃圾的对象,在回收线程暂停的过程中,变成了垃圾,那么为了解决这个问题,就有了三色标记算法。
三色标记算法:三色表示的是对象的三个状态属性有白,黑,灰三种颜色(白色表示:还没有被垃圾回收器扫描的对象,黑色表示:对象本身和对象的成员变量都已经被扫描了,灰色表示:对象本身已经被扫描,但是成员变量还没扫描完),这个算法主要是用来标记活动跟要回收的对象,可以让jvm不发生或者在remark阶段发生一次stw,在清除的时候,是清除白色标记的对象。
在CMS跟G1回收器里面,都用到了这个三色标记法,但是使用三色标记法会有一个漏标的问题,CMS跟G1的处理方法都不一样。
CMS是通过写屏障的方法,就是当已经标记了黑色的对象,当他有某个成员变量指向了白色标记对象引用的时候,把这个黑色标记变成灰色标记,但这样还是有一个并发标记,产生漏标的问题,这个算得上是一个比较隐蔽的bug,但是造成问题也是比较严重的。
举个例子:在回收线程标记A对象的时候,A对象指向B对象,B对象指向C对象,其中A对象还有两个成员变量A1,A2,在回收线程扫描完了A对象的A1,还没来得及扫描A2的时候,回收线程暂停了,然后在业务层面,B对象指向了null,然后A1指向了D对象,那这个时候,通过写屏障,会把A对象的标记变成灰色,然后回收线程继续运行,开始扫描完A2,完事之后会把A标记成了黑色,那这个时候,D对象就没办法被标记,所以D对象会被清除,为了解决这个问题,CMS在最后的remark阶段,必须从头扫描一遍,那这个时候就会产生stw
G1使用了SATB来解决这个漏标的问题,就是灰色标记的对象指向白色标记的对象,当这个指向的关系消失的时候,进行一个快照,把白色标记的对象放到GC的堆栈里面,然后在堆栈里面查询,如果有对象引用了这个白色标记的对象,那就把这个白色标记的对象标记成灰色
还有就是在java里面有四种引用类型,强引用:在代码里面直接new出来的对象属于一个强引用,如果一个对象具有强引用,JVM就没有办法对它进行回收,当内存空间不够的时候就会报OOM错误,软引用:如果一个对象只具备软引用,那么当内存空间不够的时候,JVM就会把这个对象进行回收,弱引用:如果一个对象只具备了弱引用,那么被jvm的gc线程检测到了,这个对象就会被回收
建议:在jdk1.8以后,把CMS升级为G1回收器 ,G1解决了CMS的stw问题
5.推荐视频
2.自动化回收_哔哩哔哩_bilibili
强烈推荐看下这个视频,会对底层原理理解得更加透彻