方法区的垃圾回收:
-
废弃常量的回收。
没有任何对象引用常量池的常量,而且虚拟机中也没有其他地方引用这个常量,那么这个常量就可以被回收。
-
不再使用的类型。
不再使用的类的回收是非常苛刻的。要满足下面三个条件。
- 该类的所有实例均被回收。Java堆中不存在任何该类的实例以及子类实例。
- 加载该类加载器被回收。这个条件除非是经过精心设计的场景,比如可替换类加载器的场景,否则是很难实现的。
- 该类对应的Class对象没有在任何地方被引用。无法在任何地方通过反射来访问类的方法。
分代收集理论
- 新生代。每次垃圾收集都有大量对象死去。
- 老生代。每次垃圾收集幸存的对象都会逐步晋级到老生代。
垃圾回收算法
- 标记清除算法
缺点:执行效率不稳定、容易产生内存碎片。
- 标记复制算法
每次使用内存的一半区域,垃圾清除时将存活的对象复制到另一半内存,再将这一半内存清空。
缺点:
- 如果回收的对象很少,复制开销大。
- 只能使用一半内存,太浪费了。
优点:
- 不存在内存碎片。
注意: IBM调查显示,98%的新生代对象熬不过第一轮收集。现在的虚拟机都采用这种方法作为基础。
- 标记整理算法
老生代不能用标记复制算法,因为很多都是会存活下去的对象。
将所有存活的对象都移动到一侧,然后清理掉边界以外的内存。
缺点:
- 需要暂停应用程序。
- 移动对象并更新引用是一项繁重的工作。
优点:不会产生内存碎片。
注意: 因为内存分配和访问比垃圾回收频率更高,所以移动对象反而更划算。
HotSpot 算法细节实现
根节点枚举
根节点枚举需要暂停用户线程。
虚拟机有办法知道哪个地方存放着对象引用,通过Oopmap实现。
安全点
只有特定的位置记录了Oopmap,称为安全点。
多个线程如何同时到达安全点呢?
- 抢先式中断。 如果线程中断的地方不在安全点,就继续执行。现在几乎没有jvm使用这种。
- 主动式中断。而主动式中断的思想是当垃圾收集需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志位,各个线程执行过程时会不停地主动去轮询这个标志,一旦发现中断标志为真时就自己在最近的安全点上主动中断挂起。
安全区域
如果线程sleep了怎么办?
当用户线程执行到安全区域里面的代码时,首先会标识自己已经进入了安全区域,那样当这段时间里虚拟机要发起垃圾收集时就不必去管这些已声明自己在安全区域内的线程了。当线程要离开安全区域时,它要检查虚拟机是否已经完成了根节点枚举(或者垃圾收集过程中其他需要暂停用户线程的阶段),如果完成了,那线程就当作没事发生过,继续执行;否则它就必须一直等待,直到收到可以离开安全区域的信号为止。
Garbage First垃圾收集器
G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理,这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。
G1每次收集到的内存空间都是Region大小的整数倍,这样可以有计划地避免 在整个Java堆中进行全区域的垃圾收集。
ZGC
region是不同大小的。
- 2M
- 32M
- 不限量
内存分配与回收策略
-
对象优先在Eden分配
大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。
-
大对象直接进入老年代
比如大的数组或者很长的字符串。 大对象如果放在新生代,那么频繁的复制行为会产生高额的资源开销。
-
长期存活的对象会进入老年代
对象通常在Eden区里诞生,如果经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,该对象会被移动到Survivor空间中,并且将其对象年龄设为1岁。对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15),就会被晋升到老年代中。
-
动态对象年龄判定
如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到要求的年龄。
-
空间分配担保
在Minor GC之前,虚拟机必须检查老生代最大可用连续空间是否大于新生代所有对象空间之和。如果大于,那么这次Minor GC就是安全的。如果小于,会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者-XX: HandlePromotionFailure设置不允许冒险,那这时就要改为进行一次Full GC。
如果担保失败,就会发起一次Full GC。