垃圾回收与内存分配

 

引用计数法

  • 弱分代假说:绝大多数对象都是朝生熄灭的
  • 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡
  • 跨代引用假说:跨代引用相对于同代引用来说仅占极少数

将不同生命周期的对象分配到不同的区域以便管理和提高效率,年轻代只需关注如何保留少量存活而不是去标记那些大量将要被回收的对象,就能以较低代价回收到大量空间;如果剩下的都是难以消亡的对象,虚拟机就可以使用较低的频率来回收这个区域,这就同时兼顾了垃圾收集的时间开销和空间内存的有效利用
常见的垃圾回收算法

引用计数法

在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一,当引用失效时,计数器值就减一;任何时刻计数器为0的对象就时不可能再被使用

可达性分析算法

这个算法的基本思路就是通过一系列称为GC Roots的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索
搜索过程所走过的路径称为引用链,如果某个对象到GC Roots间没有任务引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象时不可能再被使用的

在Java技术体系里面,固定课作为GC Roots的对象包括以下几种:

  • 在本地变量表中引用的对象,比如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等
  • 方法区中类静态属性引用的对象,比如Java类的引用类型静态变量.
  • 方法区中常量引用的对象,比如字符串常量池里的引用
  • 在本地方法栈JNI引用的对象
  • Java虚拟机内部的引用,如基本数据类型对于的class对象,一些常驻的异常对象(比如
  • NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器
  • 所有被同步锁(synchronized关键字)持有的对象
  • 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调,本地代码缓存等

标记-清除算法

使用引用计数法判定对象是否需要清除

 缺点:

执行效率不稳定,需要进行大量标记和清除的动作,效率随着对象的数量增长而降低
内存碎片:标记,清除之后会产生大量不连续的内存碎片,碎片太多可能导致后续程序运行过程需要分配较大的对象无法分配足够的连续内存而提前出发另一次垃圾收集

标记-复制算法

针对新生代的算法

 将内存划分为相等的两块,每次创建对象只用其中一块,当这一块内存用完了,将还存活的对象复制到另外一块,然后把已使用的内存一次清理掉

缺点:内存使用效率低
注:新生代的对象有98%熬不过第一轮收集,所以并不需要1:1的比例来划分新生代的内存空间,目前的主流算法是把新生代分为一块较大的Eden空间和两块较小的Survivor空间,每次分配内存只使用Eden和一块Survivor.发生垃圾收集时,将Eden和Survivor中仍存活的对象一次性复制到另外一块Survivor空间上,然后直接清理掉Eden和已使用的Survivor空间.HotSpot虚拟机默认Eden和Survivor的大小比例是8:1

标记-整理算法

针对老年代的算法,效率低于标记-复制算法

内存分配与回收策略

  1. 对象在新生代Eden区分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC. HotSpot虚拟机提供-XX:PretenureSizeThreshold参数,指定大于该设置值的对象直接在老年代分配,避免在Eden区及两个Survivor区直接来回复制
  2. 长期存活的对象将进入老年代:虚拟机在对象头中定义了一个对象年龄计数器,对象通常在Eden区分配,如果经过一次Minor GC仍然存活,并且能被Survivor容纳的话,该对象会被移动到Survivor空间中,并将其兑现过年了设为1岁,对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄到一定程度(默认15),就会被移到老年代中.年龄阈值可以通过参数-XX:MaxTenuringThreshold设置
  3. 动态对象年龄判断,如果Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到年龄阈值
  4. 空间分配担保:在Minor GC之前,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那这一次Minor GC可以确保是保全的.如果不成立,则虚拟机会先查看-XX:HandlePromotionFailure参数的值是否允许担保失败;如果允许,那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者-XX:HandlePromotionFailure设置不允许冒险,那这是就要改为进行一次Full GC,该参数在JDK1.6以后被废弃,只要判断"老年代可用空间">"新生代对象总和" 或者 "老年代可用空间">"历次Minor GC升入老年代对象的平均大小",两个条件满足一个,就可以直接进行Minor GC,不需要提前出发Full GC了

触发老年代GC的条件

  1. 老年代可用内存小于新生代全部对象的大小,如果没开启空间担保参数,会直接触发Full GC,所以一般空间担保参数都会打开
  2. 老年代可用内存小于历次新生代GC后进入老年代的平均对象大小,此时会提前Full GC
  3. 新生代Minor GC后存活对象大于Survivor,那么久会进入老年代,此时老年代内存不足,触发Full GC
posted @ 2020-04-21 21:08  大碗炸酱面  阅读(185)  评论(0编辑  收藏  举报