GC机制

GC机制和原理

gc(Garbage Colletion),这项技术并不是java独有的,1960诞生于MIT的Lisp是第一门真正使用动态分配和垃圾收集技术的语言。

1.哪些内存回收

2.什么时候回收

3.如何回收

 

1判定是否回收的算法:

1.1引用计数算法(*JAVA中并不使用此算法)

每个对象添加到引用计数器,每被引用一次,计数器+1,失去引用,计数器-1,当计数器在一段时间内为0时,即认为该对象可以被回收了。但是这个算法有个明显的缺陷:当两个对象相互引用,但是二者都已经没有作用时,理应把它们都回收,但是由于它们相互引用,不符合垃圾回收的条件,所以就导致无法处理掉这一块内存区域。因此,Sun的JVM并没有采用这种算法,而是采用一个叫——可达性分析算法。

1.2可达性分析算法(又叫根搜索算法)

如图所示

 

基本思想是:从一个叫GC Roots的根节点出发,向下搜索,如果一个对象不能达到GC Roots的时候,说明该对象不再被引用,可以被回收。如上图中的Object5、Object6、Object7,虽然它们三个依然相互引用,但是它们其实已经没有作用了,这样就解决了引用计数算法的缺陷。

 

在Java语言中,可作为GC Roots的对象有一下4种

1.虚拟机栈(栈帧中的局部变量表)中引用的对象。

2.方法区中类静态属性引用的对象。

3.方法区常量引用的对象

4.本地方法栈中JNI(即一般说的native方法)引用的对象。

1.3四种引用状态

在JDK1.2之前,Java中引用的定义很传统:如果引用类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。这种定义很纯粹,但是太过于狭隘,一个对象只有被引用或者没被引用两种状态。我们希望描述这样一类对象:当内存空间还足够时,则能保留在内存中;如果内存空间在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用4种,这4种引用强度依次减弱。

强引用

代码中普遍存在的类似"Object obj = new Object()"这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象

软引用

有用但是非必须的对象,在发生内存溢出之前,把这些对象列入回收范围进行第二次回收。如果回收之后还没有足够的内存,将会抛出内存溢出的异常。

弱引用

虚引用

 

注意:不一定死亡

 

1.4 回收方法区

废弃的常量

无用的类

 

2.1垃圾收集算法

标记-清除算法(Mark-Sweep)

复制算法(Copying)

当Survivor空间不够用时,需要依赖老年代进行分配担保(Handle Promotion)

标记-整理算法(Mark-Compact)

分代收集算法

大批对象死去、少量对象存活的(新生代),使用复制算法,复制成本低;对象存活率高、没有额外空间进行分配担保的(老年代),采用标记-清理算法或者标记-整理算法。

 

3.1HotSpot的算法实现

枚举根节点

安全点

安全区域

 

4.1垃圾收集器

Serial(串行)收集器

特点:单线程,简单高效,没有线程交互开销,可以获取最高的单线程收集效率

使用的算法:复制算法

收集区域:新生代

配置参数:

缺点: 需要STW(Stop The World),停顿时间长

适用场景:Client模式下的默认新生代收集器,用户桌面应用

 

ParNew收集器

特点:多线程,并行,除了使用多条线程进行垃圾收集外,其余行为和Serial收集器完全一样

使用的算法:复制算法

收集区域:新生代

配置参数:默认开启的收集线程数与CPU数量相同

可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数

缺点:会有线程交互的开销

适用场景:Server模式下的虚拟机首选的新生代收集器

 

 

Parallel Scavenge(清除污物)收集器

特点:多线程,并行,关注点和其他收集器不同,更关注吞吐量

所谓吞吐量的意思就是CPU用于运行用户代码时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总运行100分钟,垃圾收集1分钟,那吞吐量就是99%。

 

使用的算法:复制算法

收集区域:新生代

配置参数:默认开启的收集线程数与CPU数量相同

可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数

缺点:会有线程交互的开销

适用场景:Server模式下的虚拟机首选的新生代收集器

 

Serial Old收集器

特点:Serial收集器的老年代版本,单线程

使用的算法:标记-整理算法

收集区域:老年代

配置参数:

缺点:

适用场景:Client模式下的虚拟机使用

Parallel Old收集器

特点:Parallel Scavenge收集器的老年代版本

使用的算法:标记-整理算法

收集区域:老年代

配置参数:

缺点:

适用场景:在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge收集器+Parallel Old收集器的组合

 

CMS收集器

特点:CMS(Conrrurent Mark Sweep)收集器是以获取最短回收停顿时间为目标的收集器

使用的算法:标记-清除算法

(1). 初始标记,标记GCRoots能直接关联到的对象,时间很短。

(2). 并发标记,进行GCRoots Tracing(可达性分析)过程,时间很长。

(3). 重新标记,修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,时间较长。

(4). 并发清除,回收内存空间,时间很长。

其中,并发标记与并发清除两个阶段耗时最长,但是可以与用户线程并发执行。

 

收集区域:老年代

配置参数:-XX:+UseCMSCompactAtFullCollection参数来进行碎片的合并整理过程,这样会使得停顿时间变长

-XX:+CMSFullGCsBeforeCompaction,用于设置执行多少次不压缩的Full GC后,接着来一次带压缩的GC

缺点:

1. 对CPU资源非常敏感,可能会导致应用程序变慢,吞吐率下降。

2. 无法处理浮动垃圾,因为在并发清理阶段用户线程还在运行,自然就会产生新的垃圾,而在此次收集中无法收集他们,只能留到下次收集,这部分垃圾为浮动垃圾,同时,由于用户线程并发执行,所以需要预留一部分老年代空间提供并发收集时程序运行使用。

3. 由于采用的标记 - 清除算法,会产生大量的内存碎片,不利于大对象的分配,可能会提前触发一次Full GC

适用场景:在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge收集器+Parallel Old收集器的组合

 

G1收集器

(1). 并行和并发。使用多个CPU来缩短Stop The World停顿时间,与用户线程并发执行。

(2). 分代收集。独立管理整个堆,但是能够采用不同的方式去处理新创建对象和已经存活了一段时间、熬过多次GC的旧对象,以获取更好的收集效果。

(3). 空间整合。基于标记 - 整理算法,无内存碎片产生。

(4). 可预测的停顿。能简历可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。

在G1之前的垃圾收集器,收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分(可以不连续)Region的集合。

 

5.1内存分配与回收策略

当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如

果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,

即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对

象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即

from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年

龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定

),这些对象就会成为老年代。

 

对象优先在Eden区分配

 

大对象直接进入老年代

 

长期存活的对象将进入老年代

动态对象年龄判定

空间分配担保

 

 

垃圾清理触发方式

fullGC、minorGC、majorGC还有youngGC是Java垃圾处理机制(GC)的名词,区分这几个概念非常简单:

新生代进行一次垃圾清理,被称为youngGC或者minorGC。复制算法

老生代进行一次垃圾清理,被称为fullGC或者majorGC。标记-整理算法

 

新生代触发方式:

新生代采用“空闲指针”的方式来控制GC触发,指针保持最后一个在新生代分配的对象位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。

 

老生代触发方式:

1.老生代空间不足

2.Pemanet Generation(永久代--方法区)空间不足

3.GC后晋升到老生代的平均大小大于老生代剩余空间

4.手动调用System.gc()

 

 

 

参考链接:

https://www.cnblogs.com/xiaoxi/p/6486852.html GC详解

https://www.cnblogs.com/leefreeman/category/1058724.html JVM

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值