关于垃圾回收你真的了解透彻了吗?我熬夜深度剖析了一下

  • 大部分对象的生命周期都很短;
  • 其他对象则很可能会存活很长时间。

大部分死的快,其他的活的长。这个假设我们称之为弱代假设(weak generational hypothesis)。

接下来划重点。

image.png

从图中可以看到,大部分对象是朝生夕灭的,其他的则活的很久。

现在的垃圾回收器,都会在物理上或者逻辑上,把这两类对象进行区分。我们把死的快的对象所占的区域,叫作年轻代(Young generation)。把其他活的长的对象所占的区域,叫作老年代(Old generation)。

老年代在有些地方也会叫作 Tenured Generation,你在看到时明白它的意思就可以了。

image

年轻代

年轻代使用的垃圾回收算法是复制算法。因为年轻代发生 GC 后,只会有非常少的对象存活,复制这部分对象是非常高效的。

我们前面也了解到复制算法会造成一定的空间浪费,所以年轻代中间也会分很多区域。

image

如图所示,年轻代分为:一个伊甸园空间(Eden ),两个幸存者空间(Survivor )。

当年轻代中的 Eden 区分配满的时候,就会触发年轻代的 GC(Minor GC)。具体过程如下:

  • 在 Eden 区执行了第一次 GC 之后,存活的对象会被移动到其中一个 Survivor 分区(以下简称from);
  • Eden 区再次 GC,这时会采用复制算法,将 Eden 和 from 区一起清理。存活的对象会被复制到 to 区;接下来,只需要清空 from 区就可以了。

所以在这个过程中,总会有一个 Survivor 分区是空置的。Eden、from、to 的默认比例是 8:1:1,所以只会造成 10% 的空间浪费。

这个比例,是由参数 -XX:SurvivorRatio 进行配置的(默认为 8)。

一般情况下,我们只需要了解到这一层面就 OK 了。但是在平常的面试中,还有一个点会经常提到,虽然频率不太高,它就是 TLAB,我们在这里也简单介绍一下。

TLAB 的全称是 Thread Local Allocation Buffer,JVM 默认给每个线程开辟一个 buffer 区域,用来加速对象分配。这个 buffer 就放在 Eden 区中。

这个道理和 Java 语言中的 ThreadLocal 类似,避免了对公共区的操作,以及一些锁竞争。

image

对象的分配优先在 TLAB上 分配,但 TLAB 通常都很小,所以对象相对比较大的时候,会在 Eden 区的共享区域进行分配。

TLAB 是一种优化技术,类似的优化还有对象的栈上分配(这可以引出逃逸分析的话题,默认开启)。这属于非常细节的优化,不做过多介绍,但偶尔面试也会被问到。

老年代

老年代一般使用“标记-清除”、“标记-整理”算法,因为老年代的对象存活率一般是比较高的,空间又比较大,拷贝起来并不划算,还不如采取就地收集的方式。

那么,对象是怎么进入老年代的呢?有多种途径。

(1)提升(Promotion)

如果对象够老,会通过“提升”进入老年代。

关于对象老不老,是通过它的年龄(age)来判断的。每当发生一次 Minor GC,存活下来的对象年龄都会加 1。直到达到一定的阈值,就会把这些“老顽固”给提升到老年代。

这些对象如果变得不可达,直到老年代发生 GC 的时候,才会被清理掉。

这个阈值,可以通过参数 ‐XX:+MaxTenuringThreshold 进行配置,最大值是 15,因为它是用 4bit 存储的(所以网络上那些要把这个值调的很大的文章,是没有什么根据的)。

(2)分配担保

看一下年轻代的图,每次存活的对象,都会放入其中一个幸存区,这个区域默认的比例是 10%。但是我们无法保证每次存活的对象都小于 10%,当 Survivor 空间不够,就需要依赖其他内存(指老年代)进行分配担保。这个时候,对象也会直接在老年代上分配。

(3)大对象直接在老年代分配

超出某个大小的对象将直接在老年代分配。这个值是通过参数 
-XX:PretenureSizeThreshold 进行配置的。默认为 0,意思是全部首选 Eden 区进行分配。

(4)动态对象年龄判定

有的垃圾回收算法,并不要求 age 必须达到 15 才能晋升到老年代,它会使用一些动态的计算方法。比如,如果幸存区中相同年龄对象大小的和,大于幸存区的一半,大于或等于 age 的对象将会直接进入老年代。

这些动态判定一般不受外部控制,我们知道有这么回事就可以了。通过下图可以看一下一个对象的分配逻辑。

image

卡片标记(card marking)

你可以看到,对象的引用关系是一个巨大的网状。有的对象可能在 Eden 区,有的可能在老年代,那么这种跨代的引用是如何处理的呢?由于 Minor GC 是单独发生的,如果一个老年代的对象引用了它,如何确保能够让年轻代的对象存活呢?

对于是、否的判断,我们通常都会用 Bitmap(位图)和布隆过滤器来加快搜索的速度。如果你不知道这个概念就需要课后补补课了。

JVM 也是用了类似的方法。其实,老年代是被分成众多的卡页(card page)的(一般数量是 2 的次幂)。

卡表(Card Table)就是用于标记卡页状态的一个集合,每个卡表项对应一个卡页。

如果年轻代有对象分配,而且老年代有对象指向这个新对象, 那么这个老年代对象所对应内存的卡页,就会标识为 dirty,卡表只需要非常小的存储空间就可以保留这些状态。

垃圾回收时,就可以先读这个卡表,进行快速判断。

HotSpot 垃圾回收器

接下来介绍 HotSpot 的几个垃圾回收器,每种回收器都有各自的特点。我们在平常的 GC 优化时,一定要搞清楚现在用的是哪种垃圾回收器。

在此之前,我们把上面的分代垃圾回收整理成一张大图,在介绍下面的收集器时,你可以对应一下它们的位置。

image

年轻代垃圾回收器

(1)Serial 垃圾收集器

处理 GC 的只有一条线程,并且在垃圾回收的过程中暂停一切用户线程。

这可以说是最简单的垃圾回收器,但千万别以为它没有用武之地。因为简单,所以高效,它通常用在客户端应用上。因为客户端应用不会频繁创建很多对象,用户也不会感觉出明显的卡顿。相反,它使用的资源更少,也更轻量级。

(2)ParNew 垃圾收集器

ParNew 是 Serial 的多线程版本。由多条 GC 线程并行地进行垃圾清理。清理过程依然要停止用户线程。

ParNew 追求“低停顿时间”,与 Serial 唯一区别就是使用了多线程进行垃圾收集,在多 CPU 环境下性能比 Serial 会有一定程度的提升;但线程切换需要额外的开销,因此在单 CPU 环境中表现不如 Serial。

(3)Parallel Scavenge 垃圾收集器

另一个多线程版本的垃圾回收器。它与 ParNew 的主要区别是:

  • Parallel Scavenge:追求 CPU 吞吐量,能够在较短时间内完成指定任务,适合没有交互的后台计算。弱交互强计算。
  • ParNew:追求降低用户停顿时间,适合交互式应用。强交互弱计算。

老年代垃圾收集器

(1)Serial Old 垃圾收集器

与年轻代的 Serial 垃圾收集器对应,都是单线程版本,同样适合客户端使用。

年轻代的 Serial,使用复制算法。

老年代的 Old Serial,使用标记-整理算法。

(2)Parallel Old

Parallel Old 收集器是 Parallel Scavenge 的老年代版本,追求 CPU 吞吐量。

(3)CMS 垃圾收集器

CMS(Concurrent Mark Sweep)收集器是以获取最短 GC 停顿时间为目标的收集器,它在垃圾收集时使得用户线程和 GC 线程能够并发执行,因此在垃圾收集过程中用户也不会感到明显的卡顿。我们会在后面的课时详细介绍它。

长期来看,CMS 垃圾回收器,是要被 G1 等垃圾回收器替换掉的。在 Java8 之后,使用它将会抛出一个警告。

Java HotSpot™ 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.

配置参数

除了上面几个垃圾回收器,我们还有 G1、ZGC 等更加高级的垃圾回收器,它们都有专门的配置参数来使其生效。

通过 -XX:+PrintCommandLineFlags 参数,可以查看当前 Java 版本默认使用的垃圾回收器。你可以看下我的系统中 Java13 默认的收集器就是 G1。

java -XX:+PrintCommandLineFlags -version

-XX:G1ConcRefinementThreads=4 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:MinHeapSize=6815736 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC

java version “13.0.1” 2019-10-15

Java™ SE Runtime Environment (build 13.0.1+9)

Java HotSpot™ 64-Bit Server VM (build 13.0.1+9, mixed mode, sharing)

以下是一些配置参数:

  • -XX:+UseSerialGC 年轻代和老年代都用串行收集器
  • -XX:+UseParNewGC 年轻代使用 ParNew,老年代使用 Serial Old
  • -XX:+UseParallelGC 年轻代使用 ParallerGC,老年代使用 Serial Old
  • -XX:+UseParallelOldGC 新生代和老年代都使用并行收集器
  • -XX:+UseConcMarkSweepGC,表示年轻代使用 ParNew,老年代的用 CMS
  • -XX:+UseG1GC 使用 G1垃圾回收器
  • -XX:+UseZGC 使用 ZGC 垃圾回收器

为了让你有个更好的印象,请看下图。它们的关系还是比较复杂的。尤其注意 -XX:+UseParNewGC 这个参数,已经在 Java9 中就被抛弃了。很多程序(比如 ES)会报这个错误,不要感到奇怪。

image

有这么多垃圾回收器和参数,那我们到底用什么?在什么地方优化呢?

目前,虽然 Java 的版本比较高,但是使用最多的还是 Java8。从 Java8 升级到高版本的 Java 体系,是有一定成本的,所以 CMS 垃圾回收器还会持续一段时间。

线上使用最多的垃圾回收器,就有 CMS 和 G1,以及 Java8 默认的 Parallel Scavenge。

  • CMS 的设置参数:-XX:+UseConcMarkSweepGC。
  • Java8 的默认参数:-XX:+UseParallelGC。
  • Java13 的默认参数:-XX:+UseG1GC。
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

结局:总结+分享

看完美团、字节、腾讯这三家的一二三面试问题,是不是感觉问的特别多,可能咱们真的又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。

开篇有提及我可是足足背下了Java互联网工程师面试1000题,多少还是有点用的呢,换汤不换药,不管面试官怎么问你,抓住本质即可!能读到此处的都是真爱

  • Java互联网工程师面试1000题

image.png

而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的 《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题。

  • 程序员代码面试指南–IT名企算法与数据结构题目最优解

image.png

  • 其余像设计模式,建议可以看看下面这4份PDF(已经整理)

image.png

  • 更多的Java面试学习笔记如下,关于面试这一块,我额外细分出Java基础-中级-高级开发的面试+解析,以及调优笔记等等等。。。

image.png

以上所提及的全部Java面试学习的PDF及笔记,如若皆是你所需要的,那么都可发送给你!

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
我额外细分出Java基础-中级-高级开发的面试+解析,以及调优笔记等等等。。。

[外链图片转存中…(img-WL9fmvXt-1712325201837)]

以上所提及的全部Java面试学习的PDF及笔记,如若皆是你所需要的,那么都可发送给你!

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值