写点什么

训练营第九周作业 1

用户头像
仲夏
关注
发布于: 2020 年 11 月 21 日

作业一:

(至少完成一个)

  • 请简述 JVM 垃圾回收原理。

  • 设计一个秒杀系统,主要的挑战和问题有哪些?核心的架构方案或者思路有哪些?

垃圾回收的简单描述

什么是自动垃圾收集?

自动垃圾收集是查看堆内存的过程,可以识别哪些对象正在使用,哪些不是,以及删除未使用的对象。一个正在使用的对象或一个被引用的对象,意味着你的程序的某个部分仍然保持着一个指向这个对象的指针。未使用的对象或未引用的对象不再被程序的任何部分引用。所以未被引用的对象所使用的内存可以被回收。

在像 C 这样的编程语言中,分配和释放内存是一个手动过程。在 Java 中,释放内存的过程由垃圾收集器自动处理。基本过程可以描述如下。

第 1 步:标记

这个过程的第一步就是标记。这是垃圾收集器标记内存中哪些对象正在被使用,哪些对象已经没有被使用。



有用的对象显示为蓝色,没有用的对象显示为黄色。在标记阶段扫描所有对象,然后做出这个决定。如果必须扫描系统中的所有对象,这可能是非常耗时的过程。

第 2 步:普通删除

内存维护着一个空闲内存列表,每次分配空间时,会来这个列表上找到合适的空间分配。正常删除时,会把没有用到的对象的内存空间还给空闲列表。



另一种第 2 步:删除并压缩

为了进一步提高性能,除了删除未引用的对象之外,还可以压缩剩余的引用对象。 通过移动被引用的对象,这使得新的内存分配变得更容易和更快。



为什么使用分代垃圾收集?

如前所述,标记和压缩 JVM 中的所有对象效率不高。 随着越来越多的对象被分配,对象列表的增长和增长导致更长和更长的垃圾收集时间。 然而,应用程序的实证分析表明,大多数对象是短暂的。

这里给个数据的例子。



正如你所看到的,随着时间的推移对象保持存活的越来越少。 实际上,大多数对象的寿命都很短,如图左侧较高的值所示。

JVM 的分代

根据上面的对象的行为特性,我们可以总结出一个更好的方式来提高 JVM 垃圾回收的效率。所以,就把堆内存分成几种代,新生代老年代永久代(Java8 之后就没有永久代了,取而代之的是元数据 Metaspace)。



一个新的对象会被分配在新生代上,并且新的对象会在新生代里慢慢变老。当新生代的空间被占满后,就会触发一次 minor gc。假设新生代里的对象死亡率很高的话,那么新生代的垃圾回收就是很优的。一个充满死亡对象的新生代收集起来其实很快。幸存下来的对象会慢慢变老,直到可以移入老年代。

Stop the World Event——所有的新生代手机都是停止世界的事件。Stop the World Event 的意思是,所有的应用程序的线程都会被暂停,直到垃圾回收完成。新生代 GC 总是 Stop the World。

老年代是存放那些经历了多次 minor gc,年纪达到一个阈值之后的存活的对象。一般来说,会给对象设置一个年龄阈值,达到阈值之后,就会移入老年代。最后,老年代需要进行垃圾回收,就会触发一次 major gc。

Major gc 也是导致 Stop the World。在大部分情况下,major gc 是会比 minor gc 慢很多。所以,对于一个关注响应时间的应用来说,应该尽可能的降低 major gc 的次数。这里也要注意到,major gc 的停顿时间(Stop the World 的时间)是和你选取的垃圾收集器有关的。

永久代包含了 JVM 所需要的 class 和 method 的定义等元数据。永久代会随着 JVM 运行时加载的 class 而填充新的元数据。除此之外,Java SE 的类库也会被存储在这里。

如果 JVM 检测到这部分 class 不会被使用了,而且需要更多的内存空间来加载其他的 class,那么 class 也会被回收(unloaded/卸载)。这个收集包含在一次 full gc 中。(即便是在 Java8 之后,没有了所谓了永久代,取而代之的是元数据,但是,也会存在类型卸载的回收)

分代垃圾回收

现在你已经明白了为什么需要把堆细分成不同的几个代,现在是时候仔细的看看这种空间是如何工作的了。下面的图演示了在 JVM 中,对象的分配和变老的过程。

1.首先,任何对象都会被分配在 eden 区。两个 suvivor 区一开始都是空的。

这里是为了给读者介绍垃圾回收器的设计过程,和一步步的思考过程,在之后还是会有很多优化,可能会和一开始的设计意图相违背,请见谅。比如,有的对象甚至不分配到堆里(逃逸分析),有的大对象甚至会直接分配到 old 区(大对象分配),有的对象甚至会分配到堆外内存(nio 等),等等各种特殊情况。



2.当 eden 满了之后,就会触发一次 minor gc。



3.活着的对象会被移到第一个 suvovor 区(第一个第二个都是相对的)。没有被是用的对象就直接被清除了。



4.下一次 minor gc 发生时,同样的操作。没有被使用的对象被清除,活着的对象和被移到另一个 suvivor 区。而且,这些对象年龄会+1,然后被移入第二个 suvivor。所有的活着的对象都被移入这个新的 suvivor1,那么 eden 和 suvivor0 又都空了。但是,现在在 suvivor1 中,对象的年龄是不一样的。



5.下一次 minor gc,又会重复上面的步骤。不过对象是从 eden 和 suvicor1 移入到 suvivor0 中了。



6.终于,随便不断的 minor gc,对象的年龄越来越大,达到了阈值(这里是 8)时,他们会晋升带老年代。



7.随着更多的 minor gc,也有更多的对象晋升到老年代。



8.上面已经涵盖了新生代的整个过程。最后,老年代需要进行一次 major gc 来清除,压缩老年代的空间。



用户头像

仲夏

关注

还未添加个人签名 2018.05.16 加入

还未添加个人简介

评论

发布
暂无评论
训练营第九周作业 1