JVM学习(二)垃圾回收器和内存分配策略

博客中相关代码的git 地址:

JVM学习(一)自动内存管理机制地址:https://blog.csdn.net/qq_40119805/article/details/107399842
JVM学习(二)垃圾回收器和内存分配策略地址:https://blog.csdn.net/qq_40119805/article/details/107432575
JVM学习(三)虚拟机执行子系统地址:https://blog.csdn.net/qq_40119805/article/details/107456166
JVM学习(四)虚拟机类加载机制地址:https://blog.csdn.net/qq_40119805/article/details/107456166
JVM学习(五)JVM并发内存模型地址:https://blog.csdn.net/qq_40119805/article/details/107456166

本章主要介绍:
1.判断对象是否死亡;2.垃圾回收算法;3.垃圾回收器;4.对象的内存分配;

1.对象已死吗

要想做垃圾回收,第一步就应该区判断哪些对象已经死亡;

1.1 引用计数

引用计数器法:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;

1.2 可达性分析

可达性分析算法:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。

gc根有四种:

  • 方法区中的静态属性的引用;
  • 栈帧中引用的对象
  • 方法区中引用的常量
  • 本地栈中Native方法的引用

1.3 多种引用类型

强引用:发生 gc 的时候不会被回收。
软引用:有用但不是必须的对象,在发生内存溢出之前会被回收。
弱引用:有用但不是必须的对象,在下一次GC时会被回收。
虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用PhantomReference 实现虚引用;

2.垃圾回收算法

标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。

2.1标记清除

标记无用对象,然后进行清除回收。

标记-清除算法(Mark-Sweep)是一种常见的基础垃圾收集算法,它将垃圾收集分为两个阶段:

标记阶段:标记出可以回收的对象。
清除阶段:回收被标记的对象所占用的空间。
标记-清除算法之所以是基础的,是因为后面讲到的垃圾收集算法都是在此算法的基础上进行改进的。

优点:实现简单,不需要对象进行移动。

缺点:标记、清除过程效率低,产生大量不连续的内存碎片,提高了垃圾回收的频率。

2.2 标记整理

为了解决标记-清除算法的效率不高的问题,产生了复制算法。它把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾收集时,遍历当前使用的区域,把存活对象复制到另外一个区域中,最后将当前使用的区域的可回收的对象进行回收。

优点:按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片。

缺点:可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制。

2.3 复制算法

在新生代中可以使用复制算法,但是在老年代就不能选择复制算法了,因为老年代的对象存活率会较高,这样会有较多的复制操作,导致效率变低。标记-清除算法可以应用在老年代中,但是它效率不高,在内存回收后容易产生大量内存碎片。因此就出现了一种标记-整理算法(Mark-Compact)算法,与标记-整理算法不同的是,在标记可回收的对象后将所有存活的对象压缩到内存的一端,使他们紧凑的排列在一起,然后对端边界以外的内存进行回收。回收后,已用和未用的内存都各自一边。

优点:解决了标记-清理算法存在的内存碎片问题。

缺点:仍需要进行局部对象移动,一定程度上降低了效率。

2.4 为什么新生代使用复制算法而不是标记整理算法

值得注意的是标记整理算法不仅仅要移动存活对象,还需要移动死亡对象,在新生代中存活对象少,使用复制算法仅需要移动死亡对象,可以节省很多时间;

3. 垃圾回收器

新生代回收器:Serial、ParNew、Parallel Scavenge
老年代回收器:Serial Old、Parallel Old、CMS
整堆回收器:G1
新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

3.1 Serial收集器(复制算法)

最古老的收集器,单线程,效率低,当然是在多核cpu的情况下,在单核cpu的服务器上因为线程调度问题可能表现比其他收集器更优秀;

3.2 ParNew收集器 (复制算法)

Serial收集器的多线程版本,相对于上个版本没啥改进,但是用的还是挺多的,因为只有它能够配合CMS 收集器(老年代);

3.3 Parallel Scavenge收集器 (复制算法)

Parallel Scavenge是一款吞吐量优先的收集器,它可以设置gc收集时间,收集器每次收集尽量不会超过这个时间,但是时间设置的越短,gc触发越频繁。还可以设置吞吐量和自适应调节策略,他会根据系统的情况,自己调整Eden和 survivor的大小和晋升老年代的年龄;

3.4 Serial Old收集器 (标记-整理算法)

Serial收集器的老年代版本,现在用的比较少了,主要用来做CMS收集器的后备预案;

3.5 Parallel Old收集器 (标记-整理算法)

Parallel Scavenge 收集器的老年代版本;这个收集器1.6才发布,在这之前Parallel Scavenge只能配合Serial收集器进行工作;导致老年代效率低;

3.6 CMS收集器(标记-清除算法)

CMS是一款以获取最短停顿为目标的收集器,主要应用于互联网;
在CMS中并发标记和并发清理可以和用户线程一起进行所以它停顿时间短;
但是也引发了三个问题:
1.收集器和用户线程争夺cpu资源;
2.用户线程不停止无法清理正在生成的垃圾,并且因为用户线程不停止,还要预留一部分空间给用户线程,如果在清理过程中预留空间不足收集会失败且会触发后备预案Serial Old收集器;
3.他是标记清除算法会产生碎片,虽然说可以开启内存整理的配置,但是在整理过程中性能会下降;

3.7 G1收集器 (标记-整理算法)

当前最nb的收集器:有着和CMS一样的并发特征且不需要其他收集器配合,自己就能完成新-老年代的垃圾收集,且使用的是整理算法不会产生空间碎片,并且可以预测停顿时间;

4.内存分配策略

4.1 对象优先分配在Eden区

多数情况,对象都在新生代 Eden 区分配。当 Eden 区分配没有足够的空间进行分配时,虚拟机将会发起一次 Minor GC。如果本次 GC 后还是没有足够的空间,则将启用分配担保机制在老年代中分配内存。

  • Minor GC 是指发生在新生代的 GC,因为 Java 对象大多都是朝生夕死,所有 Minor GC 非常频繁,一般回收速度也非常快;
  • Major GC/Full GC 是指发生在老年代的 GC,出现了 Major GC 通常会伴随至少一次 Minor GC。Major GC 的速度通常会比 Minor GC 慢 10 倍以上。

4.2 大对象直接进入老年代

所谓大对象是指需要大量连续内存空间的对象,频繁出现大对象是致命的,会导致在内存还有不少空间的情况下提前触发 GC 以获取足够的连续空间来安置新对象。

前面我们介绍过新生代使用的是标记-清除算法来处理垃圾回收的,如果大对象直接在新生代分配就会导致 Eden 区和两个 Survivor 区之间发生大量的内存复制。因此对于大对象都会直接在老年代进行分配。

4.3 长期存活的对象进入老年代

对象在 Survivor 区中每「熬过」一次 Minor GC 年龄就加 1,当年龄达到一定程度(默认 15) 就会被晋升到老年代。

4.5 动态年龄判定

为了适应不同的程序的内存状况,并不是年龄大于15的才会进入老年代,当同一年龄的对象超过了Survivor的一半,那么比该年龄大的对象会进入老年代;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值