对象存活判定算法:2种
1.引用计数算法:给对象添加一个引用计数器,有地方引用它时,计数器就加1,引用失效时,计数器减1.任何时刻,计数器为0的对象,是不可能再被使用的
优点:实现简单,判断效率也很高
缺点:很难解决对象之间相互循环引用的问题,对象A,B相互引用,引用计数不为0
2.可达性分析算法:
通过一系列称为"GC Roots"的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链,则证明这个对象是不可用的
两次标记过程:不存在引用链->第一次标记并进行一次筛选,筛选条件是此对象是否有必要执行finalize()方法.当对象没有覆盖finalize()方法或者finalize方法已经被执行过,虚拟机将这两种情况都视为"没有必要执行".->有必要执行,那么这个对象会放置在F-Queue队列中,并在稍后由一个虚拟机自动建立的,低优先级的线程去执行它.->稍后GC将对F-Queue中的对象进行第二次小规模的标记.如果对象要在finalize()中成功拯救自己,只要重新与引用链上的任何一个对象建立关系即可,那在第二次标记的时候,它将被移除出"即将回收"集合.如果这时候对象还没逃脱,那它基本上就真的被回收了
可作为GC Roots的对象包括以下几种:
a.虚拟机栈中引用的对象
b.方法区中类静态属性引用的对象
c.方法区中常量引用的对象
d.本地方法栈中navtive引用的对象
垃圾收集算法:4种
1.标记-清除算法:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象.
缺点: a.效率问题,标记和清除的效率都不高
b.空间问题,标记清除后会产生大量不连续的内存碎片,导致以后需要分配较大对象时,无法找到足够的连续内存而不得不触发另一次垃圾回收
2.复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完,就将还存活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉,这样使得每次都是对整个半区进行内存回收
优点: a.实现简单,运行高效
缺点: a.将内存缩小为原来的一半,代价太高
3.标记-整理算法:标记过程与"标记-清除算法一样",但后续操作不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存
4.分代收集算法:根据对象的存活周期不同将内存划分为几块,一般是把java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法.
exam:在新生代中,每次垃圾收集时都有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存货对象的复制成本就可以完成收集,而老年代中对象成活率高,没有额外空间对他分配担保,就必须使用"标记-清除"算法或"标记-整理"算法来进行回收
HotSpot中的垃圾收集器:7个,可组合使用
1.Serial收集器: ->开启方式:-XX:+UseSerialGC
a.单线程收集器
b.在进行垃圾收集的时候,必须暂停其他所有的工作线程,直到收集结束
c.Client模式下,默认新生代收集器
d.优点: 简单而高效,对于限定单个CPU的环境,Serial收集器没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率
场景:在用户的桌面应用场景中,分配给虚拟机管理的内存不会很大,收集几十兆甚至一两百兆的新生代,停顿时间完全可以控制在几十毫秒最多100毫秒以内,只要不是频繁发生,这点停顿是可以接受的.所以Serial收集器对运行在Client模式下的虚拟机来说是一个很好的选择
2.ParNew收集器:
ParNew收集器是Serial收集器的多线程版本,其他并没有太多创新之处,但它却是许多运行在Server模式下的虚拟机首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集器之外,只有它能与CMS收集器配合工作
3.Parallel Scavenge收集器:吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
a.新生代收集器
b.复制算法
c.并行的多线程收集器
d.提供两个参数用于精确控制吞吐量: -XX:MaxGCPauseMillis-控制最大垃圾收集停顿时间(大于0的毫秒数) -XX:GCTimeRatio-直接设置吞吐量大小(大于0且小于100的整数,默认值为99,就是允许最大1%,即1/(1+99)的垃圾收集时间)
e.吞吐量优先的收集器
f.自适应调节策略
g.-XX:+UseAdaptiveSizePolicy-开启自适应策略
4.Serial Old收集器:
a.是Serial收集器的老年代版本.
b.单线程收集器
c.使用标记-整理算法
d.主要意义也是给Client模式下的虚拟机使用
e.Server模式下有两大用途: 与Parallel Scavenge收集器搭配使用,另一种是作为CMS收集器的后背预案,在并发手机发生Concurrent Mode Failure时使用
5.Parellel Old收集器:
a.是Parallel Scavenge收集器的老年代版本
b.多线程
c.使用标记-整理算法
d.在注重吞吐量和CPU资源敏感的场合,都可以优先考虑Parallel Scavenge+Parellel Old组合
6.CMS收集器: ->开启:-XX:+UseParNewGC,-XX:+UseConcMarkSweepGC
a.是一款以获取最短回收停顿时间为目标的收集器
b.目前很大一部分的java应用集中在互联网站或B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验,CMS就非常适合这类应用的需求
c.基于标记-清除算法实现
d.四个步骤:
初始标记 -> 仅仅只是标记下GC Roots能直接关联到的对象,速度很快
并发标记 -> 进行GC Roots 跟踪的过程
重新标记 -> 为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,停顿时间比初始标记稍长,但远比并发标记的时间短
并发清除 ->
整个过程耗时最长的c,d两步可以与用户线程一起工作,所以从总体上来说,CMS的内存回收过程是与用户线程并发执行的
e. 优点:并发收集,低停顿
缺点: CMS收集器对CPU资源非常敏感
CMS收集器无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败而导致另一次Full GC的产生
标记-清除算法的缺点,收集结束时会产生大量的空间碎片,
-XX:UseCMSCompactAtFullCollection开关(默认开启)->用于在CMS收集器顶不住要进行FullGC时开启内存碎片的合并整理过程,内存整理的过程是无法并发的,空间碎片问题没有了,但停顿时间不得不变长
-XX:CMSFullGCsBeforeCompaction->用于设置执行多少次不压缩的Full GC后,来一次带压缩的(默认是0,表示每次进入Full GC时都进行碎片整理)
7.G1收集器:收集器技术最前沿的成果之一
a.特点: 并行与并发->G1能充分利用多CPU,多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行
分代收集->与其他收集器一样,分代概念在G1中仍然得以保留.虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间,熬过多次GC的旧对象以获取更好的收集效果
空间整合->与CMS的"标记-清理"算法不同,G1从整体来看是基于"标记-整理"算法实现的收集器,从局部(两个Region之间)上来看是基于"复制"算法实现的,但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存.这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前出发下一次GC
可预测的停顿->这是G1相对于CMS的另一大优势,降低停顿时间是G1和 CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了
b.G1收集器运作步骤: ->开启:-XX:+UseG1GC,相对于CMS优势是,内存碎片的产生率大大降低
初始标记->标记一下GC Roota能直接关联到的对象,并且修改TAMS的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这阶段需要停顿线程,但耗时很短.
并发标记->从GC Roots开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行.
最终标记->为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并行执行.
筛选回收->首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率.
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f88e5386406a16d197840adec5ef20f8.png)
Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决了两个问题:
1.给对象分配内存
2.回收分配给对象的内存
对象晋升老年代:
1.虚拟机给每个对象定义一个对象年龄(Age)计数器,如果对象在Eden出生并经历过第一次Minor GC后仍然存活,并且能够被Survivor容纳的话,将被移动到Survivor空间,并且对象年龄设为1.对象在Survivor区中每"熬过"一次Minor GC,年龄就增加1岁,当年龄增加到一定程度(默认15岁),将会被晋升到老年代中.可以通过参数 -XX:MaxTenuringThreshold设置
2.在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄
Minor GC:只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC
引用种类:4种引用强度依次减弱
1.强引用:
代码中普遍存在的,类似"Object o=new Object()"这类的,只要引用还存在,垃圾搜集器永远不会回收被引用的对象
2.软引用
描述一些还有用但非必须的对象.对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之内进行二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常.提供了SoftReference类来实现软引用
3.弱引用
也是用来描述非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象,只能生存到下次垃圾搜集发生之前.当垃圾搜集器工作时,无论当前内存是否足够,都会被回收掉.提供了WeakReference类来实现弱引用
4.虚引用
最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生存空间构成影响,也无法通过虚引用来取得一个对象的实例.为一个对象设置虚引用关联的唯一目的是这个对象被收集器回收时收到一个系统通知.提供了PhantomReference类来实现弱引用
方法区的垃圾回收
永久代的垃圾搜集主要回收两部分内容:废弃常量和无用的类