JVM(三)- 垃圾收集器及内存分配策略(附代码示例)

1、对象是否已死

通过对象是否已死来决定要不要回收该对象。判断对象是否已死的算法有:引用计数算法、可达性分析算法。

1.1、引用计数算法

简单描述引用计数算法,对每个对象一个引用计数器,被引用就+1,引用失效-1,计数器为0就不可能再被引用。其优缺点如下:

  • 优点
    • 实现简单,效率高;
    • 实时性高,无需等到内存不够的时候,才开始回收
    • 在垃圾回收过程中对象无需挂起。如果申请内存时,内存不足,直接报OutOfMember。
    • 区域性:更新对象的计数器时,只是影响到该对象,不会扫描全部对象。
  • 缺点
    • 每次对象引用时,都需要去更新计数器,有一点时间开销。
    • 浪费CPU资源,既是内存够哦用,仍然在运行时进行计数器的统计。
    • 无法解决循环引用问题。(最大缺陷)不能解决对象间互相循环引用的问题;
      所以主流的java虚拟机未选用该算法来管理内存。

1. 2、可达性分析算法

主流的商用语言java、C#等都是通过可达性分析算法来判断对象是否存活。基本实现思想为通过“GC Roots”作为起始点,向下搜索,走过的路径成为引用链“Reference Chain”,如果对象到“GC Roots”没有引用链相连,那该对象可回收。如图:
在这里插入图片描述
GC Roots对象包括

  • 虚拟机栈中引用的对象
  • 方法区中静态属性引用的对象(1.8之后移至元空间)
  • 方法区中常量引用的对象(1.8之后移至元空间)
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象
    拓展
    • 强引用(Strong Reference)
      开发中最常见的引用,如:

      Object obj = new Object();
      
    • 软引用(Soft Reference)
      软引用需要专门的声明,如:

      SoftReference<String> str = new SoftReference<String>("hello");
      

      软引用关联的对象在内存不足时被回收
      适合用来做缓存;

    • 弱引用(Weak Reference)
      弱引用也需要专门声明,例如:

      WeakReference<String> str = new WeakReference<String>("hello");
      

      被弱引用关联的对象每次GC都会被回收
      弱引用最常见的用途是实现可自动清理的集合或者队列;

    • 虚引用(Phantom Reference)
      也需要专门声明,如:

      	PhantomReference<String> phantom = new PhantomReference<>(new String("hello"), new ReferenceQueue<>());
      

      虚引用是最弱的引用,完全不影响对象的生存空间,唯一的作用是对象被回收时发一个通知;

1.3 起死回生

对象被判定死亡(即不可达)后,不会被立即回收,经过两次标记才会被回收。注:finalize()方法只会被调用一次。该方法代价高昂,不建议使用 。如图:
在这里插入图片描述

2、垃圾收集算法

主要包含标记-清除算法、复制算法、标记-整理算法、分代收集算法。

2.1、标记-清除算法

分两个步骤:

  • 标记:从根节点开始标记非垃圾对象,即前面1.2降到的可达性分析算法;
  • 清除:清除所有未被标记的对象;

执行过程如图:
在这里插入图片描述

  • 优点
    • 解决了内存虚幻利用的问题;
  • 缺点
    • 效率低,标记和清除都需要遍历所有的对象;
    • 内存碎片太多,内存不连贯,较大对象分配内存无法找到连续可用内存,会提前出发GC;

2.2 复制算法:

将可用内存分为相等的两块A/B,每次只使用A,A内存不足时,将A中或者的对象拷贝到B上,将A清空,A/B调换位置。
在这里插入图片描述

  • 优点:实现简单、(对象存活率较低时)运行高效;
  • 缺点
    • 内存缩小为原来的一半;
    • 对象存活率较高时,复制操作频繁,效率会降低

2.3 标记整理算法

针对老年代,标记过程与“标记-清除算法”一样,让所有存货的对象向一端移动,清理掉端边界以外的内存。如下图:
在这里插入图片描述

2.4 分代收集算法

  • 当前商业虚拟机的垃圾收集都是用分代收集算法。
  • 根据对象存活周期不同,对内存进行划分,针对不同区域采取不同算法;
  • java堆分为新生代、老年代;
  • 新生代,朝生夕死,用复制算法;
  • 老年代:标记-清理或标记整理

3 HotSpot 的GC垃圾收集器

  • 没有最好的牢记收集器,更加没有万能的垃圾收集器,只能选择对具体应用最适合的;
  • HotSpot包含的收集器如下,有连线的部分是可以组合使用:
    在这里插入图片描述
  • GC组合使用总结
GC组合 Minor GC Major GC/Full GC 描述
-XX:+UserSerialGC Serial收集器串行回收(单线程) Serial Old收集器串行回收(单线程) 该选项可以手动指定Serial收集器 +Serial Old收集器组合执行内存回收,多用于Client应用
-XX:+UserParNewGC ParNew收集器并行回收(多线程) Serial Old收集器串行回收(单线程) 该选项可以手动指定ParNew收集器 +Serial Old收集器组合执行内存回收
-XX:+UserParallelGC Parallel收集器并行回收(多线程) Serial Old收集器串行回收(单线程) 该选项可以手动指定Parallel收集器 +Serial Old收集器组合执行内存回收
-XX:+UserParallelOldGC Parallel收集器并行回收(多线程) Parallel Old收集器并行回收(多线程) 该选项可以手动指定Parallel收集器 +Parallel Old收集器组合执行内存回收
-XX:+UserConcMarkSweepGC ParNew收集器并行回收(多线程) 默认使用CMS收集器并发回收,备用采用Serial Old收集器串行回收(单线程) 默认指定ParNew收集器 + CMS +Serial Old 收集器组合执行内存回收。先使用ParNew + CMS,当出现ConcurrentMode Fail 或Promotion Failed时,采用ParNew + Serial Old
-XX:+UserG1GC G1收集器并发、并行执行内存回收 G1收集器并发、并行执行内存回收

注:

  • minor GC:新生代的垃圾回收;
  • major GC:也叫作full GC,老年代的垃圾回收。

名称解释

1、安全点、安全区域
2、并发、并行

并发和并行都是并发编程中的概念,结合垃圾收集器的语境解释如下:

  • 并行(Parallel):指多条垃圾收集器线程并行工作,但此时用户线程处于等待状态;
  • 并发(Concurrent):指用户线程与垃圾收集器线程同时运行(不一定并行,可能交替执行),用户程序继续运行,垃圾收集程序运行于另一CPU。
3、吞吐量

吞吐量 = 运行用户代码的时间 / (运行代码的时间 + 垃圾收集的时间)。
如:虚拟机总共运行100分钟,垃圾回收1分钟,吞吐量就是99%。

3.1 Serial收集器

采用复制算法的新生代收集器,单线程。垃圾回收时,stop the word ,多用于Client应用;

  • 优点
    • 与其他收集器的单线程相比,对单个CPU来说,无线程交互开销,简单高效;
  • ** 缺点**:
    • 垃圾回收时,停止所有线程,对于除桌面应用之外的其他应用来说,stop the word时间太长
  • Serial / Serial Old收集器的运行过程如下:
    在这里插入图片描述

3.2 ParNew收集器

Serial的多线程版本,同样是采用复制算法新生代并行收集器。

  • 优点
    • 多CPU场景下,效率高于Serial收集器;
    • 除Serial收集器外,目前只有ParNew能与CMS收集器配合工作;
  • 缺点
    • 单CPU场景下,增加了线程开销,效率低于Serial收集器;
  • 与Serial Old收集器配合使用,如图:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值