JVM垃圾回收

in 编程
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

如何判断一个对象已经死亡?

  1. 引用计数器:
    1. 每个对象都维护一个引用计数器,被引用时+1,引用失效时-1,如果该计数器为0表示不再被使用,表示可以被销毁了;
    2. 这种方式无法解决一种场景,两个对象相互引用,按照这种方式的判断逻辑,两个对象都不会被回收;
  2. 根搜索算法:
    1. 将一些可以定义为“根(GC roots)”的对象作为起点,在整个VM范围内,搜索被“根”(或者通过中间对象引用)引用的的对象,搜索完成之后,能搜索到的,认为这些对象还需要使用,不能被销毁;没搜索到的对象认为不再被使用了,可以销毁;
    2. 根,就是一组必须活跃的引用,程序运行需要的从这些“根”发起,然后去创建需要的对象;
    3. 能够被作为根的对象
      1. 当前活跃的虚拟机栈中指向堆里对象的引用:当前正在被调用的方法中的局部变量、参数;
      2. 静态变量;
      3. 常量;
      4. 本地方法应用的对象;
    4. 引用分类
      1. 强应用(Strong):永远不会被回收,例如:Object obj = new Object();
      2. 软引用(SoftReference):有用,但是非必须的对象,在内存即将发生溢出之前被回收;
        1. 使用场景:常用数据加载到内存,而且可以随时从数据库重新记载的数据,如果内存吃紧,就先回收,下一次使用时,在重新加载到内存;可以作为外置的内存缓存(Redis,memCache等)的上一级;
      3. 弱引用(WeakReference):只能生存到下一次内存回收之前,先放入一个队列,下一次内存回收时会被清除,只能活一个周期;
      4. 虚引用(PhantomReference):不会决定对象的生命周期,唯一的目的是希望在这个对象被回收时收到一个通知;可以用作GC活动的监听;
        1. 重要对象回收监听,进行日志统计;
        2. 系统gc监听 因为虚引用每次GC都会被回收,那么我们就可以通过虚引用来判断gc的频率,如果频率过大,内存使用可能存在问题,才导致了系统gc频繁调用;

 

java对象在什么时候被回收?

1. 对象已经死亡;

2. JVM虚拟机内存不够用,需要回收内存;

 

为什么分代回收?

分代回收可以换个说法,叫分区域回收或者部分回收;将堆分成两个或者更多区域,一个在A区域的对象被A或者其他区域的对象引用,那么就暂时不销毁这个对象,如果没有引用,直接销毁;这种说法下,其他区域的所有对象都成为了A区域对象的“GC root”,这时候不会去关心其他区域的这些“GC roots”对象和“原始的GC roots”是否有引用;

在一些垃圾回收算法中,将堆分成了两部分:年轻代、老年代

年轻代:清理了指定的次数之后,还需要存活的对象,会被标记为(迁移到)老年代;对年轻代的对象清理掉频率比较高,年轻代的对象存活率也相对较低;;

老年代:老年代的清理频率较低;

 

回收方法区

  1. 废弃的常量
  2. 无用的类

条件:

  1. 该类的所有实例都已被回收;
  2. 该类的classLoader应该被回收;
  3. 该类 对应的java.lang.Class对象没有在人和地方被引用,无法在任何地方通过反射访问该类;

使用场景:大量使用反射、动态代理、cglig、JSP、OSGI;

垃圾收集算法

  • 标记-清理算法
    • 先扫描一遍,标记出需要回收的对象,然后再清理标记为回收的对象;
    • 缺点:
      • 标记、清理的效率不高;
      • 清理之后的内存空间不连续,会产生大量的内存碎片;
  • 标记-整理算法
    • 对标记-清理算法的改进,标记之后,将所有存活的对象将内存空间的一段移动,然后清理掉边界以外的空间;
    • 适合回收存活周期比较长的对象;
  • 复制算法
    • 将内存区域分成等比例的两份,新对象的分配都在其中一份中,在空间满的时候,将需要存活下来的对象复制到另一份中,清空已使用的那一份内存区域;
    • 优点:简单、高效;
    • 缺点:只能使用一半内存;但是考虑到大部分对象的死亡率都很高,可以修改比例、分区方式;
  • 分代收集算法
    • 这不是一个基本算法,而是根据实际情况,选择不同的算法;
    • 分代的标准是对象的存活周期,周期短的叫新生代,周期长的叫老年代;
    • 对于新生代,考虑其周期短,可以选择效率比较高的复制算法;
    • 对于老年代,可以选择标记整理算法;

 

 

垃圾收集器

  • Serial
    • 在GC线程启动时,用户线程全部出于停止状态,等GC任务结束,用户线程恢复执行;
    • 一个GC线程;
    • 新生代收集器;
    • 适合在client模式下使用;
  • Serial Old
    • Serial 的老年代版本,单线程,使用“标记-整理”算法,主要使用在client模式下;
  • ParNew
    • Serial的改进版本,GC任务多线程,其他和Serial一致;
    • 适合在Server(多核>2核)模式下使用;
    • 可以和CMS配合使用;
    • 新生代收集器;

 

  • Parallel Scavenge
    • 使用复制算法的新生代收集器;
    • 以结果为导向(高吞吐量,两个参数:最大垃圾收集停顿时间-MaxGCPauseMillis、吞吐量-GCTimeRatio)的并行多线程收集器;
    • 因为以结果为导向,所以,他可以做到自适应动态调节一些参数;

 

  • Parallel Old
    • Parallel Scavenge的老年代版本,使用多线程的“标记-整理”算法;
  • CMS
    • 最短停顿时间为目标的垃圾收集器,使用了“标记-清理”算法;
    • 一个收集周期经过四个阶段:
      • 初始标记:独占资源,单线程,标记和GC roots有直接引用关系的对象,速度很快;
      • 并发标记:和用户线程并发执行,标记出所有和GC roots有引用关系的对象;
      • 重新标记:独占资源,但是是多线程,修正在并发标记阶段,已经标记的但是发生变化的对象,时间比初始标记长,比并发标记要短的多;
      • 并发清理:和用户线程并发执行;

  • 优点:并发执行、低停顿;
  • 缺点:
    • 对CPU很敏感:所有多线程任务,对CPU都敏感;
    • 会出现浮动垃圾:在并发标记阶段,用户线程产生的新垃圾无法在本次回收周期中一起回收;
    • 会有不连续的内存碎片:这是采用的标记-清理算法导致的;
  • G1
    • 使用“标记-整理”算法,所以,没有CMS的不连续内存碎片
    • 不只是区分年轻代、老年代两个区域,而是划分城了很多更加细小的区域,可以做到只回收某一个区域的内存;
    • 因为可以做到精确控制只回收一个区域,所以,他可以精确控制停顿时间

 

垃圾回收策略

  • 优先分配到Edge区域
    • 如果时间不够分配,则发起一次Minor GC ,之后如果够就分配到Edge中;
  • 大对象分配到老年代:
    • 如果minor GC之后,Edge中还是没有塞下,那么直接进入老年代。还有一种情况,如果这个对象的大小超过配置的值(-XXPretenuresizeThreshld,该参数只对Serial、Serial old有效),那么直接进入老年代;这种一般发生在给大对象分配内存的情况下;
  • 长期存活的对象进入老年代(对象晋升):
    • 对Edge其中一个Survivor中的对象做回收时,如果能成功塞到另一个survivor中,那么这些对象都age就+1;
    • 如果某个对象的age,超过一个指定的值(-XX:MaxTenuringThreshold,默认15),则把这个对象移到老年代;
  • 对象年龄动态判断
    • 如果在Survivor中,某个年龄的对象所占用的总大小超过了Survivor点一半,这将>=该age的对象全部移到老年代;
  • 空间分配担保
    • 目标,尽可能少的进行Full GC;
    • 在将要Minor GC之前,会检查,老年代的剩余空间是否大于新生代所有对象之和:
      • 小于:则Full GC,放弃Minor GC;
      • 大于:则检查一个参数HandlePromotionFailure(表示是否接受Minor GC失败):
        • HandlePromotionFailure == false:则Full GC;
        • HandlePromotionFailure == true:则检查老年代的剩余空间大小是否大于之前历次晋升到老年代的对象都平均大小:
          • 大于:则Minor GC;
          • 小于:则Full GC;

 

 

 

 

 

Copying GC

HotSpot VM里的young GC有多种实现,但其基本算法都是copying GC,正好适合在这里讲解。

Copying GC在HotSpot VM里叫做“Scavenging”。请题主参考我的一个老帖:关于HotSpot VM的Serial GC中的minor GC的“简单”讲解,里面我介绍了Copying GC的经典的Cheney算法,以及其与HotSpot VM的serial GC的关系。

HotSpot VM里Java对象的对象头(object header)有2个字段(2-word):

_mark  // mark word
_klass // klass pointer

其中mark word用于存储多种信息,例如对象的identity hash code、锁状态、full GC时的mark状态等等。在执行Copying GC时,如果一个对象被拷贝了,那么该对象的mark word可以存储它的forwarding pointer指向新的拷贝。

HotSpot的对象模型里,一个对象是由VM掌管的metadata部分和用户代码掌管的instance data结合构成的。其中每个对象都有作为metadata的对象头(object header)和作为对象内容的instance data。它们是作为一个整体存在的。

在copying GC去copy对象的时候是整个对象一起拷贝的,包括对象头和对象内容。

在copy发生后,原版对象(拷贝前)的对象头里的mark word会持有一个指针指向新对象(拷贝后)。此时新老对象的对象内容是完全一致的,只是老对象的对象头的原本的状态(例如是否持有锁啊之类)已经不需要维护了(这些信息都由拷贝后的新对象维护),所以对象头的空间就可以安全地腾出来放forwarding pointer。

关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9
扫一扫关注公众号添加购物返利助手,领红包
Comments are closed.

推荐使用阿里云服务器

超多优惠券

服务器最低一折,一年不到100!

朕已阅去看看