架构师训练营第 9 周作业

用户头像
在野
关注
发布于: 2020 年 08 月 03 日

1. 简述 JVM 垃圾回收原理



服务器的内存资源是有限的,不能无限使用,为了更合理高效地利用内存资源,于是有了JVM的垃圾回收,回收后的内存可以继续使用。

垃圾判断算法



在JVM中,那些不可达的对象就是垃圾。什么是不可达呢?其实就是没有办法再引用到的对象。在JVM中有两种主要的垃圾判断算法:引用计数法和可达性分析法



引用计数法:给每个对象一个引用计数器,没引用一次+1,引用失效后-1,当计数为0时表示该对象没有任何引用,成了“垃圾”。不过引用计数法有个缺点,很难解决对象之间互相循环引用的问题



可达性分析法:通过一系列称为 GC Roots 的对象作为起点,从这些节点开始向下搜索,如果从所有 GC Roots 到一个对象都不可达,则证明此对象就是“垃圾”。下面列举可以作为 GC Roots 的对象:

  • Java虚拟机栈中被引用的对象,各个线程调用的参数、局部变量、临时变量等。

  • 方法区中类静态属性引用的对象,比如引用类型的静态变量。

  • 方法区中常量引用的对象。

  • 本地方法栈中所引用的对象。

  • Java虚拟机内部的引用,基本数据类型对应的Class对象,一些常驻的异常对象。

  • 被同步锁(synchronized)持有的对象。

垃圾回收算法



垃圾找出来以后,就要回收垃圾,下面介绍垃圾收集算法。

标记-清除算法

它分为“标记”和“清除”两个阶段:首先标记出所需回收的对象,在标记完成后统一回收掉所有被标记的对象,它的标记过程其实就是前面的可达性分析法中判定垃圾对象的标记过程。下图为“标记-清除”算法的示意图:

下图为使用“标记-清除”算法回收前后的状态:

标记-清除算法的缺点:

  • 标记和清除过程的效率都不高,这种方法需要使用一个空闲列表来记录所有的空闲区域以及大小,对空闲列表的管理会增加分配对象时的工作量;

  • 标记清除后会产生大量不连续的内存碎片,虽然空闲区域的大小是足够的,但却可能没有一个单一区域能够满足这次分配所需的大小,因此本次分配还是会失败,不得不触发另一次垃圾收集动作。

复制算法

复制算法首先将可用内存分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活的对象连续复制到另一块上面,然后把使用过的内存空间一次清理掉。下图为使用复制算法回收前后的状态:

复制算法的缺点是能使用的内存缩小为原来的一半。

标记-整理算法

标记-整理算法分为“标记”和“整理”两个阶段,执行过程:

  1. 标记:首先标记出所有需要回收的对象

  2. 整理:让所有的存活的对象都向一端移动,然后直接清除掉边界以外的内存

下图为使用标记-整理算法回收前后的状态:

标记-整理算法的缺点:效率偏低(两遍扫描,指针需要调整)

常用的垃圾回收器

  1. Serial 年轻代 串行回收

  2. PS 年轻代 并行回收

  3. ParNew 年轻代 配合CMS的并行回收

  4. SerialOld 老年代 串行回收

  5. ParallelOld 老年代 并行回收

  6. ConcurrentMarkSweep 老年代 并发的,垃圾回收和应用程序同时运行,降低STW的时间

  7. G1:引入了分区的思路,弱化了分代的概念

  8. ZGC:能支持4TB内存,号称STW在10ms之内

什么时候触发GC

新生代的垃圾回收叫 Minor GC,当下列情况发生时,会触发 Minor GC:

  • 当Eden区和From Survivor区空间不足时

Full GC 对整个对进行垃圾回收,当下列情况发生时,会触发 Major GC:

  • 执行 system.gc() 的时候,系统建议执行Full GC,但是不一定执行

  • 老年代空间不足时

  • 统计得到的 Minor GC 晋升到老年代的平均大小大于老年代的剩余空间时

  • Minor GC 之后 to survior放不下,放入老年代,老年代也放不下,触发Full GC, 或者新生代有对象放入老年代,老年代放不下,触发FullGC

  • new 一个大对象,新生代放不下,直接到老年代,老年代空间也不够,触发Full GC



新对象分配空间触发GC情况图:



2. 设计一个秒杀系统

  • 主要的挑战和问题有哪些?

  • 秒杀的资源有限,如何做到不超卖

  • 瞬时并发高,服务要保证高可用

  • 前端、后端、存储在短时内的负荷会非常大

  • 不能影响其他业务流程的正常使用

  • 核心的架构方案或者思路有哪些?

  • 前端资源静态化,在没有开始秒杀之前,尽量少的请求传到服务端

  • 接入端负载均衡,将请求负载到后台多台服务器上

  • 同步操作异步化

  • 使用消息队列用于削峰

  • 如果抢完,迅速返回失败

  • 保护数据库资源,尽量采用缓存



发布于: 2020 年 08 月 03 日 阅读数: 62
用户头像

在野

关注

还未添加个人签名 2012.03.11 加入

还未添加个人简介

评论 (1 条评论)

发布
用户头像
1. 虚拟机栈中的引用的对象
2. 方法区中的类静态属性引用的对象
3. 方法区中的常量引用的对象。
4. 本地方法栈中的JNI(native方法)引用的对象

"GC Roots"的对象

2020 年 08 月 23 日 18:47
回复
没有更多了
架构师训练营第 9 周作业