GC垃圾收集器(GC [grabage collection])
1.哪些内存(垃圾)需要回收
1.java堆
2.方法区
因为虚拟机栈,本地方法栈,程序计数器都是属于线程私有的,它们会随着线程的创建而分配内存,线程结束自动清除,所以我们不需要太多的关注
2.什么时候回收
1.java堆
1.堆中存放的是Java创建的实例对象,因此在回收之前必须确定哪些对象是"存活"的,哪些对象是"死亡"的,回收的就是我们的"死亡对象"
判断方法
1.引用计数法:给对象一个引用计数器,每引用一次计数器就加1(就是使用这个对象的时候),失效的时候就减1。当引用计数器的值为0的时候,则说明这个对象不会被使用了,进入"死亡"状态,就表示可以被回收了
缺点是有回环导致无法判断(不能解决循环依赖)
2.可达性分析法:
以Gc Roots作为根,可以找到a,b,并且a b又可以找到自己下面相对应的,这时候就判断它们是"存活"的。但GC Root无法通过任何节点来找到A,AA,AAA,这就造成了不可达,就会判断它们是可回收的,当GC触发的时候就会回收A,AA,AAA三个对象
GC Roots是什么?
可以作为GC Roots的对象包括四种:
1.虚拟机栈中的引用对象
2.方法区中静态变量引用的对象
3.方法区中常量引用的对象
4.本地方法栈中引用的对象
不可达就是通过上面四种情况都没有联系的
引用的定义:C类里面引用了一个String类型的a
引用分为四种
1.强引用:只要引用存在,垃圾回收就永远都不会回收掉(宁愿抛出OutOfMemoryError错误)
我们可以直接使用obj取得对应的对象,例如obj.equals(new Object());像这种情况equals中的new Object()对象释放的情况只能跟着obj的释放而释放
2.软引用
这里其实是softReference对obj的一个软引用,如果obj对象被标记为需要回收的对象时,就会返回null。软引用主要用于实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,就不能去真实的地方取值,可以提升速度;当内存不足时,就将缓存的这部分数据清除,再从真实的数据地来取这些数据
3.弱引用
运行后
比软引用的生命周期更短。在垃圾回收器扫描的过程中,只要发现了弱引用的对象,不管内存是否足够,都会回收它的内存。但是由于垃圾回收器是一个优先级很低的线程,因为不一定很快能发现那些拥有弱引用的对象
4.虚引用
这里个人感觉写的非常好:https://juejin.im/post/5b82c02df265da436152f5ad
2.方法区
一般不要求对方法区的垃圾进行回收;因为复杂度比较高,成本比较大,效率较低
3.怎么回收
运行各种垃圾收集算法
1.标记清除算法
在可回收的对象上打一个标记,然后回收的时候就回收这些标记的对象即可
2.标记整理算法
在标记清除的算法的基础上对内存排一下序;不会像标记清除算法那样产生内存碎片(分布不平均,当放入的对象大于某一块空间的时候就会浪费时间,而且还可能压根找不到)
3.复制算法
只使用一半区域,当内存回收的时候,将存活对象移动到另一边来且排好序,然后将其它的一边直接清掉
4.分代收集算法
垃圾收集器
分别串行收集器(单个线程)和并行收集器(多个线程)
1.年轻代
1.serial
最古老的新生代收集器而且还是单线程的收集器,运行的时候所有的线程都会停掉,只使用它自己(一般不使用);使用的算法是标记清除算法和复制算法;属于串型的收集器(当我们对它进行标记的时候,这个时候GC线程只会开一个)
2.parNew
相当于parNew的多线程版本(多条线程去执行);采用的算法是复制算法
3.parallel Scavenge
多线程并且是并行的垃圾收集;采用的算法是复制算法
2.老年代
1.serial Old
单线程的垃圾收集器,采用的是标记整理算法
2.Parallel Old
多线程的收集器,采用的是标记整理算法;注重吞吐量,停顿的时间比较长
3.CMS(jdk1.8之前用的比较多)
采用的是标记清除;STW停顿的时间短,也是一个并发收集器
收集过程
1.初始标记:会进行STW(StopWork,停掉用户的所有线程),在初始标记的时候只会标记GC Roots能够关联到的对象(直接通过根达到的,而不是间接的),速度快
2.并发标记:找间接关联的,不止局限于直接通过根达到的
3.重新标记:修正并发标记期间因为用户线程继续运行而导致的标记产生变动的那一部分的标记记录
4.并发清除:有用户线程在执行同时也有我们的GC线程在清除垃圾
CMS收集器(web项目就是用到了这个)的内存回收过程是与用户线程并发执行的
CMS的缺点:
1.对CPU资源敏感
2.没有办法处理浮动的垃圾,多次的full GC
3.采用的是标记清除算法,会产生大量的碎片空间,分配大对象的时候不利
组合使用:
例如你使用的是Serial这款新生代的垃圾收集器,那么你老年代就可以使用CMS或Serial Old(默认使用Serial Old);但是不能使用Parallel Old;G1是jdk1.8之后出现的(目的是替换CMD,JDK1.9已经默认的是G1了)
G1(Grabage First)收集器:
特点
1.类似于CMS收集器,并行的垃圾收集器
2.适合于对象比较多
每一个小格子里面有很多Card组成,还有REST
Card:
REST:保存了对象之间的引用
开启G1:-XX:+UseG1GC
新生代采用的是复制算法,老年代采用的是标记整理
执行的步骤
1.初始标记:也是标记直接达根的对象(initial mark)
2.根分区扫描(Root scan)
3.并发标记(Concurrent Mark)
4.重新标记(remark)
5.清除(clean)