写点什么

架构师训练营 -week09- 作业

用户头像
大刘
关注
发布于: 2020 年 11 月 18 日
架构师训练营 -week09-作业

本周作业:

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

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


作业1:请简述 JVM 垃圾回收原理。

自动垃圾回收就是将 JVM 堆中的已经不再被使用的对象清理掉,释放内存资源。

  1. 回收发生在哪里?

    JVM 的内存区域中,程序计数器、虚拟机栈和本地方法栈这 3 个区域是线程私有的,随着线程的创建而创建,销毁而销毁;栈中的栈帧随着方法的进入和退出进行入栈和出栈操作,每个栈帧中分配多少内存基本是在类结构确定下来的时候就已知的,因此这三个区域的内存分配和回收都具有确定性。那么垃圾回收的重点就是关注堆和方法区中的内存了,堆中的回收主要是对象的回收,方法区的回收主要是废弃常量和无用的类的回收。



  1. 如何确定哪些对象是可以被清理的?

   一般一个对象不再被引用,就代表该对象可以被回收。目前有以下两种算法可以判断该对象是否可以被回收。

  • 引用计数算法:这种算法是通过一个对象的引用计数器来判断该对象是否被引用了。每当对象被引用,引用计数器就会加 1;每当引用失效,计数器就会减 1。当对象的引用计数器的值为 0 时,就说明该对象不再被引用,可以被回收了。这里强调一点,虽然引用计数算法的实现简单,判断效率也很高,但它存在着对象之间相互循环引用的问题。

  • 可达性分析算法:GC Roots 是该算法的基础,GC Roots 是所有对象的根对象,在 JVM 加载时,会创建一些普通对象引用正常对象。这些对象作为正常对象的起始点,在垃圾回收时,会从这些 GC Roots 开始向下搜索,当一个对象到 GC Roots 没有任何引用链相连时,就证明此对象是不可用的。目前 HotSpot 虚拟机采用的就是这种算法。

    具体过程是:从线程栈帧中的局部变量,或者是方法区的静态变量出发,将这些变量引用的对象进行标记,然后看这些被标记的对象是否引用了其他对象,继续进行标记,所有被标记过的对象都是被使用的对象,而那些没有被标记的对象就是可回收的垃圾对象了。可达性分析算法其实是一个引用标记算法。



  1. 回收的方法有哪些?

进行完标记以后,JVM 就会对垃圾对象占用的内存进行回收,回收主要有三种方法。

  • 第一种方式是清理:将垃圾对象占据的内存清理掉,其实 JVM 并不会真的将这些垃圾内存进行清理,而是将这些垃圾对象占用的内存空间标记为空闲,记录在一个空闲列表里,当应用程序需要创建新对象的时候,就从空闲列表中找一段空闲内存分配给这个新对象。但这样做有一个很明显的缺陷,由于垃圾对象是散落在内存空间各处的,所以标记出来的空闲空间也是不连续的,当应用程序创建一个数组需要申请一段连续的大内存空间时,即使堆空间中有足够的空闲空间,也无法为应用程序分配内存。

  • 第二种方式是压缩:从堆空间的头部开始,将存活的对象拷贝放在一段连续的内存空间中,那么其余的空间就是连续的空闲空间。

  • 第三种方法是复制:将堆空间分成两部分,只在其中一部分创建对象,当这个部分空间用完的时候,将标记过的可用对象复制到另一个空间中。JVM 将这两个空间分别命名为 from 区域和 to 区域。当对象从 from 区域复制到 to 区域后,两个区域交换名称引用,继续在 from 区域创建对象,直到 from 区域满。



回收前:



清理法:

压缩法:

复制法:



  1. 回收过程

    JVM 在具体进行垃圾回收的时候,会进行分代回收。绝大多数的 Java 对象存活时间都非常短,很多时候就是在一个方法内创建对象,对象引用放在栈中,当方法调用结束,栈帧出栈的时候,这个对象就失去引用了,成为垃圾。针对这种情况,JVM 将堆空间分成新生代(young)和老年代(old)两个区域,创建对象的时候,只在新生代创建,当新生代空间不足的时候,只对新生代进行垃圾回收,这样需要处理的内存空间就比较小,垃圾回收速度就比较快。

    新生代又分为 Eden 区、From 区和 To 区三个区域,每次垃圾回收都是扫描 Eden 区和 From 区,将存活对象复制到 To 区,然后交换 From 区和 To 区的名称引用,下次垃圾回收的时候继续将存活对象从 From 区复制到 To 区。当一个对象经过几次新生代垃圾回收,也就是几次从 From 区复制到 To 区以后,依然存活,那么这个对象就会被复制到老年代区域。

    当老年代空间已满,也就是无法将新生代中多次复制后依然存活的对象复制进去的时候,就会对新生代和老年代的内存空间进行一次全量垃圾回收,即 Full GC。所以根据应用程序的对象存活时间,合理设置老年代和新生代的空间比例对 JVM 垃圾回收的性能有很大影响,JVM 设置老年代新生代比例的参数是 -XX:NewRatio。

JDK8默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 即:新生代 ( Young ) = 1/3 的堆空间大小。





  1. JVM回收器算法:串行、并行、并发、G1

JVM 中,具体执行垃圾回收的垃圾回收器有四种。

  • 第一种是 Serial 串行垃圾回收器,这是 JVM 早期的垃圾回收器,只有一个线程执行垃圾回收。

  • 第二种是 Parallel 并行垃圾回收器,它启动多线程执行垃圾回收。如果 JVM 运行在多核 CPU 上,那么显然并行垃圾回收要比串行垃圾回收效率高。在串行和并行垃圾回收过程中,当垃圾回收线程工作的时候,必须要停止用户线程的工作,否则可能会导致对象的引用标记错乱,因此垃圾回收过程也被称为 stop the world,在用户视角看来,所有的程序都不再执行,整个世界都停止了。

  • 第三种 CMS 并发垃圾回收器,在垃圾回收的某些阶段,垃圾回收线程和用户线程可以并发运行,因此对用户线程的影响较小。Web 应用这类对用户响应时间比较敏感的场景,适用 CMS 垃圾回收器。

  • 最后一种是 G1 垃圾回收器,它将整个堆空间分成多个子区域,然后在这些子区域上各自独立进行垃圾回收,在回收过程中垃圾回收线程和用户线程也是并发运行。G1 综合了以前几种垃圾回收器的优势,适用于各种场景,是未来主要的垃圾回收器。




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

秒杀系统,主要的特点:

  1. 短时间内有超高的并发请求涌向系统

2. 绝大部分的请求最终不会成交,最后成交的订单只占很小的比例



核心的处理思路:

1. 采用分层过滤的方式,在每一层都挡掉一部分请求,使得落在后端系统的压力逐步变小。

2. 秒杀模块尽量独立,从访问域名、网关、应用部署、服务器资源都独立实现。一些必要的关联,例如跟后台交易系统或者支付系统关联功能,放在最后层,也就是到达流量尽量小的模块。

3. 对于关联的系统,通过消息队列进行解耦,由消息队列来完成缓冲的作用

4. 善用缓存,减少对数据库的访问压力。

5. 为了反爬虫,制定对应的安全策略,例如随机的订单URL,秒杀前不允许访问下单页面等。



处理步骤:

1. 前端客户请求:因为秒杀新增的网络带宽,必须和运营商重新购买或者租借。为了减轻网站服务器的压力,需要将秒杀商品页面静态化,缓存在CDN,同时需要和CDN服务商临时租借新增的出口带宽。



2.前端秒杀页面:秒杀按钮在开始前进行置灰,开始后才可以进行点击,并且为了防止爬虫拿到订单URL,在开始后传入随机参数加以验证。

使用JavaScript脚本控制,在秒杀商品静态页面中加入一个JavaScript文件引用,该JavaScript文件中包含 秒杀开始标志为否;当秒杀开始的时候生成一个新的JavaScript文件(文件名保持不变,只是内容不一样),更新秒杀开始标志为是,加入下单页面的URL及随机数参数(这个随机数只会产生一个,即所有人看到的URL都是同一个,服务器端可以用redis这种分布式缓存服务器来保存随机数),并被用户浏览器加载,控制秒杀商品页面的展示。这个JavaScript文件的加载可以加上随机版本号(例如xx.js?v=32353823),这样就不会被浏览器、CDN和反向代理服务器缓存。



  1. 秒杀器的应对:

秒杀器一般下单个购买及其迅速,可以通过校验码达到一定的方法,这就要求校验码足够安全,不容易被破解。

采用的方式有:秒杀专用验证码,电视公布验证码,秒杀答题。



4. 网关层进行限流



5. 进行下订单的前置检查,检查全局已提交订单数目:

    已超过秒杀商品总数,返回已结束页面给用户;

    未超过秒杀商品总数,提交到子订单系统;



6. 为了避免绕过前端,直接通过URL进行操作,需要在前后端进行时间同步,并且在秒杀的后台任务增加时间控制。



7. 商品库存信息放置在redis中,利用redis的分布式锁,来避免超卖的情况产生。

方式一:乐观锁

    update auctionauctions set quantity = #inQuantity# where auctionid = #itemId# and quantity = #dbQuantity#

方式二:尝试减库存,扣减成功后再下单操作

    update auctionauctions set quantity = quantity-#count# where auctionid = #itemId# and quantity >= #count#



应急预案:

1. 提前部署好冗余的机器数量,应对访问流量超出预期

2. 如果是秒杀服务反应慢,则进行熔断措施。或者关闭非核心应用系统。

3. 如果出现无法补救的错误,则关闭前端入口,统一302定位到类似“秒杀已结束”的页面。



用户头像

大刘

关注

大道至简,知易行难 2017.12.27 加入

想成为合格架构师的架构师

评论

发布
暂无评论
架构师训练营 -week09-作业