GC详述

JVM在将进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收都是指新生代。
针对HotSpot VM的实现,它里面的GC按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(Full GC)

部分收集:不是完整收集整个Java堆的垃圾收集。其中又分为:
     新生代收集(Minor GC  / Young GC):只是新生代的垃圾收集
     老年代收集(Major GC / Old GC): 只是老年代的垃圾收集
        目前,只有CMS GC 会有单独收集老年代的行为
        注意,很多时候Major GC会和Full GC 混淆使用,需要具体分辨是老年代回收还是整堆回收;
     混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集。
       目前,只有G1 GC会有这种行为
整堆收集(Full GC):收集整个java堆和方法区的垃圾收集。

年轻代GC(Minor GC)触发机制:

当年轻代空间不足时,就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。
因为Java对象大多都具备朝生夕灭的特性,所以Minor GC 非常频繁,一般回收速度也比较快。
Minor GC会引发STW ,暂停其它用户的线程,等垃圾回收机制,用户线程才恢复运行。

老年代GC(Major GC/Full GC)触发机制:

指发生在老年代的GC,对象从老年代消失时,我们说”Major GC“或”Full GC“发生了
出现了Major GC,经常会伴随至少一次的Minor GC
  也就是在老年代空间不足时,会先尝试触发Minor GC.如果之后空间还不足,则触发Major GC
  Major GC的速度一般会比Minor GC慢10倍以上,STW的时间更长。
  如果Major GC后,内存还不足,就报OOM了。
  Major GC的速度一般会比Minor GC慢10倍以上。

Full GC触发机制:

1.调用System.gc()时,系统建议执行Full GC,但是不必然执行;
2.老年代空间不足;
3.方法区空间不足;
4.通过Minor GC后进入老年代的平均大小大于老年代的可用内存;
5.由Eden区、survivor space0(From Space)区向survivor space1(To Space)区复制时,对象大小小于To Space 可用内存,则把该对象转存到老年代,且老年代的可用内存大于该对象大小
说明:Full GC时开发或调优中尽量要避免的,这样暂时时间会短一些;
  

垃圾回收相关算法:
1.标记阶段:引用计数算法(Java 不采用)
垃圾标记阶段:对象存活的判断

1.在堆里存放着几乎所有的Java对象实例,在GC垃圾执行回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有被标记为已经死亡的对象,GC才会执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程我们可以称为垃圾标记阶段;
2.那么在JVM中究竟如何标记一个死亡对象呢?简单来说,当一个对象已经不再被任何的存活对象继续引用时,就可以宣判为已经死亡。
3.判断对象存活一般两种方式:引用计数算法和可达性分析算法;
4.引用计数法比较简单:对每个对象保存一个整型的引用计数器属性。用于记录对象被引用的情况;
5.对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1;当引用失效时,引用计数器就减1.只要对象A的引用计数器的值为0,即表示对象A不可能在被使用,可进行回收。
有点:实现简单,垃圾对象便于辨识;判断效率高,回收没有延迟性(随时回收)。
缺点:
1.它需要单独的字段存储计数器,这样的做法增加了存储空间的开销。
2.每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销。
3.引用计数器有一个严重的问题,即无法处理循环引用的情况(引发内存泄漏)。这是一个致命的缺陷,
导致在Java的垃圾回收器没有使用这类算法。
Python 采用这类算法:
1.手动解除循环引用问题
2.使用弱引用weakref,来解决循环引用的问题

2.标记阶段:可达性分析算法也称追踪性垃圾回收(Java 采用)

1.可以有效解决引用计数算法中循环引用问题,防止内存泄漏的发生
2.可达性分析算法是以根对象集合(GC Roots 一组必须活跃的引用)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标是否可达
3.只用可达性分析算法后,内存中的存活对象会被跟对象集合直接或间接连接着,搜索所走过的路劲称为引用链
4.如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象已经死亡,可以标记为垃圾对象
5.在可达性分析算法中,只有能够被跟对象集合直接或者间接连接的对象才是存活对象;
GC Roots 包括以下几类情况:
1.虚拟机栈引用的对象:
比如:各个线程被调用的方法中使用此的参数、局部变量等。
2.本地方法栈JNI引用的对象
3.方法区中静态属性引用的对象(7之后再堆区):字符串常量池里的引用
4.所有被同步锁synchronized持有的对象
5.Java虚拟机内部的引用:基本数据类型对应的Class对象
6.反映java虚拟机内部情况的JMXBean\JVMTI中注册的回调、本地代码缓存
由于Root采用栈方式存放变量和指针,所以如果一个指针,它保存了堆内存里面的对象。但是自己又不存在堆内存里面,那它就是一个Root
注意:
如果要使用可达性分析算法来判断内存是否可回收,那么分析工作必须在一个能保障一致性的快照中进行。这点不满足的话分析结果的准确性就无法保证。
这点也是导致GC进行时必须”Stop The World“的一个重要原因。
即使是号称(几乎)不会发生停顿的CMS收集器中,枚举跟节点时也是必须要停顿的。

3.对象的finalization机制

1.Java 语言提供了对象终止机制来允许开发人员提供对象被销毁之前的自定义的逻辑处理。
2.当垃圾回收器发现没有引用指向一个对象,即:垃圾回收此对象之前,总会先调用这个对象的finalize()方法
3.finalize()方法允许在子类中被重写,用于在对象被回收时进行资源释放。通常在这个方法中进行
一些资源释放和清理的工作,比如关闭文件,套接字和数据库连接等;
4.永远不要主动调用某个对象的finalize()方法,应该交给垃圾回收机制调用。理由包括下面3点:
  1.finalize()时可能会导致对象复活。
  2.finalize()方法的执行时间时没有保障的,它完全由GC线程决定,极端情况下,若不发生GC,则finalize()方法将没有执行机会。
  3.一个槽糕的finalize()会严重影响GC的性能。
5.从功能上来说,finalize()方法与C++中的析构函数比较相似,但是Java采用的是基于垃圾回收器的自动内存管理机制,所以finalize()方法在本质上不同于C++中的析构函数;
6.由于finalize()的方法的存在,虚拟机中的对象一般处于三种可能的状态。
如果从所有根节点都无法访问到某个对象,说明对象已经不再使用了。一般来说,此对象需要被回收。
但事实上,也并非”非死不可“的,这时候它们暂时处于”缓刑“阶段。一个无法触及的对象有可能在某
一个条件下”复活“自己,如果这样,那么对它的回收就是不合理的,为此,定义虚拟机中的对象可能
三种状态:
  1.可触及的:从根节点开始,可以到达这个对象
  2.可复活的:对象的所有引用都被释放,但是对象有可能在finalize()中复活。
  3.不可触及的:对象的finalzie()被调用,并且没有复活,那么就会进入不可触及状态。不可触及的对象不可能被复活,因为finalize()只会被调用一次。
判定一个对象objA是否可回收,至少要经历标记过程:
1.如果对象objA到GC Roots 没有引用链,则进行第一次标记。
2.进行帅选,判断此对象是否必要执行finalize()方法。
   1:如果对象objA没有重写finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机视为”没有必要执行“,objA则被判定为不可触及的。
   2.如果对象objA重写了finalize()方法,且还未执行时,那么objA会被插入到F-Queue队列中,由一个虚拟机创建的、低风险级的Finalizer线程触发其finalize()方法执行;
   3.finalize()方法是对象逃脱死亡的最后机会,稍微GC会对F-Queue队列中的对象进行第二次标记。如果objA会被移出”即将回收集合“。之后,对象会再次出现没有引用存在的情况。在这个情况下,finalize方法不会被再次调用,对象会直接变成不可触及的状态,也就是说,一个对象的finalize方法会被调用一次

4.MAT与Jprofiler的GC Roots溯源

MAT:一款功能强大的Java堆内存分析器,用于查找内存泄漏以及查看内存消耗情况
下载链接:http://www.eclipse.org/mat/
获取dump文件:
1.jps->jmap -dump:format=b,live,file=tets1.bin 13068(pid)
2.使用JVisualVM导出
捕获的heap dump文件是一个临时文件,关闭JVisualVM后自动删除,若要保留,需要将其另存为文件。
可通过以下方法捕获heap dump:
    1.在左侧”Application“(应用程序)子窗口中右击相应的应用程序,选择Heap Dump
    2.Moonitor(监视)子标签页中点击Heap Dump按钮。
本地应用程序的Heap dumps作为应用程序标签页的一个子标签页打开。同时,heap dump在左侧的Application(应用程序)栏中对应一个含有时间戳的节点,右击这个节点选择save as(另存为)即可将heap dump保存到本地    
生成堆空间dump文件:-XX:HeapDumpOnOutOfMemoryError(产生OOM时会生成dump文件) 使用
Jprofiler查看

5.清除阶段:标记-清除算法 存在碎片化问题

当成功区分出内存中存活对象和死亡对象后,GC接下啦的任务就是要执行垃圾回收,释放掉无用对象
所占用的内存空间,以便有足够的可用内存空间为新对象分配内存!
执行过程:当堆中的有效空间被耗尽的时候,就会停止整个程序(stw),然后进行两项工作,第一项则是标记,第二项则是清除
标记:Collector从中引用根节点开始遍历,标记所有被引用的对象。一般时在对象的Header中记录为可达对象。
清除:Collector对堆内存从头到尾进行线性遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收;
缺点:
1.效率不算高
2.在进行GC的时候,需要停止整个应用程序,导致用户体验差。
3.这种方式清理出来的空闲内存是不连续的,产生内存碎片。需要维护一个空闲列表;
注意:何为清除
这里所谓的清除并不是真的置空,而是把需要清除的对象地址保存在空闲的地址列表里,下次有新的对象需要加载时,判断垃圾的位置空间足够,如果够,就存放。

6.清除阶段:复制算法 效率最高

背景:为了解决标记清除算法在垃圾回收效率方面的缺陷
核心思想:
将活着的内存空间分为两块,每次只使用其中一快,在垃圾回收时将证在使用的内存中的存活对象复制
到未被使用的内存快中,之后清除证在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃
圾回收;
优点:没有标记和清除的过程,实现简单,运行高效
复制过去以后保证空间的连续性,不会出现碎片问题。
缺点:
此算法的缺点也是明显的,就是需要两倍的内存空间;
对于G1这种分拆称为大量region的GC,复制而不是移动,意味着GC需要维护region之间对象引用关
系,不管是内存占用或者时间开销也不小;
特别的:
如果系统中的垃圾对象很多,复制算法需要复制的存活对象数量并不会太大,或者说非常

7.清除阶段:标记-压缩算法 老年代 解决碎片化问题

背景:
复制算法的高效性是建立在存活对象少、垃圾对象多的前提下。这种情况在新生代经常发生,但是在老
老年代,更经常见的情况是大部分对象都是存活对象。如果依然使用复制算法。由于存活对象较多,复
制的成本较高。因此,基于老年代回收垃圾的特性。需要使用其他的算法。
标记-清除算法的确可以应用在老年代中,但是该算法不仅执行小笼包低。而且在执行完内存回收后还
会产生内存碎片,所以JVM的设计者需要在此基础上进行改进。标记-=压缩算法由此诞生;
执行过程:
1。第一阶段跟标记清除算法一样。从根节点开始标记所有被引用对象
2.第二阶段将所有的存活对象压缩到内存的一端,按顺序排放。
3.之后,清理边界外所有的空间;解决碎片化

8.分代收集算法

分代收集算法,是基于这样的一个事实:不同的对象的声明周期是不一样的,因此,不同生命周期的对
象可以采取不同的收集方式,以便提高回收效率。一般是把JAVA堆分成新生代和老年代,这样就可以
根据各个年代的特点使用不同的回收算法,以提高回收的效率;

9.增量收集算法、分区算法

以上算法,在来及回收过程中都会出现STW的状态,为解决STW延时长这个问题,出现了增量收集算法,分区算法。
基本思想:
如果一次性将所有的垃圾进行处理,需要造成系统长时间的停顿,那么就可以让垃圾收集线程和应用线
程交替执行。每次,垃圾收集线程只收集小片区域的内存空间,接着切换到应用程序线程。依次反复,
直到垃圾收集完成。总的来说:增量收集算法的基础仍是传统的标记-清除和复制算法。增量收集算法
 通过对线程间冲突的妥善处理,允许垃圾收集线程以分阶段的方式完成标记或复制工作;
 分代算法根据生命周期来划分
 分区算法是将整个堆空间划分成连续的不同小区间region.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值