一、如何判断对象为垃圾
1)引用计数算法(已淘汰):
原理:
对象中存在一个引用计数器
对象被引用时,计数器+1; 引用失效时,计数器-1
计数器为0时,对象被回收
淘汰原因:无法解决对象间相互引用的问题。
当外界不访问A,B两个对象,且对象A,B相互引用时,计数器不为0,则无法回收
2)可达性分析算法:
思想:
定义GC Root作为引用链起点,向下搜索。
被搜索到的节点对象判定为活
搜不到的节点对象将被回收
可作为GC Root的对象
1)虚拟机栈中局部常量表中对象
2)方法区中的类静态属性所引用的对象
3)方法区中的常量所引用的对象
4)本地方法栈中所引用的对象
一个对象的死亡判定需经过两次标记:
1)第一次标记:
可达性分析后,发现该对象没有与GCRoot相连。进行第一次标记与筛选
筛选条件:对象是否有必要执行finalize()方法
判定没有必要执行的两种情况:
1)对象没有覆盖finalize()方法
2)finalize()方法已经被调用过。
2)第二次标记:
对于重写了且并没有执行finalize()方法的对象,将其放置在一个F-Queue队列中,并在稍后由一个由虚拟机自动建立的低优先级的Finalizer线程去执行它。
此处执行只保证执行该方法,但是不保证等待该方法执行结束。
这样子设计是为了系统的稳定性和健壮性考虑,以免该方法执行时间较长或者死循环导致系统崩溃。
在此之后,系统会对对象进行第二次标记。
如果在第一次标记之后的对象在执行finalize()方法时没有被引用到一个新的变量,这该对象将被回收掉。
二、回收策略
1)标记-清除算法
算法思想:先标记需要清除的对象(使用可达性分析判断),后清除
缺点:
1)效率问题:标记与清除两个过程效率都不高
2)空间问题: 清除后会产生大量不连续的内存碎片
2)复制算法
算法思想:
1)将堆区域分为两部分,每次只使用其中一块。
2)当被使用的内存用完时,将内存中仍存活的对象复制并排列到另一块区域中,
3)一次清空使用过的内存
实际应用:
缺点:内存闲置大
注:堆区域的实际划分:Eden和Survivor的大小比例是8:1。
原因:新生代中的对象98%是“朝生夕死”的。
3)标记-整理算法
算法思想:
1)在内存中划出界限,
2)存活对象往界限一端移动,可回收对象往另一端移动
3)清空界限的一端(被回收的一端)
4)分代收集算法
思想:
1)根据对象不同年代实施不同回收算法
2)新生代采用复制算法
3)老年代采用 标记-清除/标记-整理算法
三、垃圾回收器
1)Serial收集器
单线程收集器(整体性能低,单个效率高)
多用于桌面应用场景中
2)ParNew收集器
多线程
缩短停顿时间
3)Parallel Scavenge收集器
目标:达到一个可控制的吞吐量(Throughput)
吞吐量=运行用户代码时间 /(运行用户代码时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,吞吐量就是99%。
停顿时间越短对于需要与用户交互的程序来说越好,良好的响应速度能提升用户的体验;
高吞吐量可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适合在后台运算而不太需要太多交互的任务。
4)Cms收集器
一种以获取最短回收停顿时间为目标的收集器
CMS收集器是基于“标记-清除”算法实现的。它的运作过程相对前面几种收集器来说更复杂一些,整个过程分为4个步骤:
1)初始标记 --Stop The World
2)并发标记
3)重新标记 --Stop The World
4)并发清除
优点:并发收集,低停顿。
缺点:
1)CMS收集器对CPU资源非常敏感
2)CMS收集器无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。
3)CMS是基于“标记-清除”算法实现的收集器,收集结束时会有大量空间碎片产生。
5)G1收集器
步骤:
1)初始标记
2)并发标记
3)最终标记
4)筛选回收
优点:
1)并行与并发
2)分代收集
3)空间整理 (标记整理算法,复制算法)
4)可预测的停顿
四、内存分配
对象的内存分配:分配堆内存
优先策略:
1)启动本地线程分配缓冲时,优先TLAB
2)优先Eden
3)大对象直接进入老年代 大对象:需要大量连续内存空间的Java对象
4)长期存活的对象将进入老年代
5)空间分配担保
检查老年代最大可用空间是否可以容纳新生代所有对象(防止新生代全部晋升时放不下)。
可以容纳,则MinorGC可以安全执行。
不能容纳,检查是否允许担保失败。
允许则检查 老年代最大可用空间 是否大于 历次晋升到老年代的对象的平均大小。
是则尝试进行MinorGC;
小于或者MinorGC失败,则会发起一次FullGC清理老年代。
栈上分配:
在方法体中声明的变量以及创建的对象,从该线程所使用的栈中分配空间。
逃逸:
指在某个方法之内创建的对象,除了在方法体之内被引用之外,还在方法体之外被其它变量引用到。
在该方法执行完毕之后,由于其被其它变量引用,该对象将无法被GC回收。
这种情况被称为逃逸
逃逸分析: 分析对象的作用域
当作用域为方法体内时,则认为没有发生逃逸,则可被回收···········
四种引用:
1)强引用:类似Object obj = new Object()的引用。强引用的对象永远不会被回收
2)软引用:描述一些有用但非必需的对象。 在发生内存溢出异常之前,会把这些对象列进回收范围进行第二次回收
3)弱引用:被引用对象只能生存到下一次垃圾收集之前。 无论内存是否足够,都会被回收
4)虚引用:唯一作用:在对象被回收之前收到一个系统通知