架构师训练营 1 期第 9 周:性能优化(三)- 作业
一、请简述 JVM 垃圾回收原理
JVM 垃圾回收就是将 JVM 堆中的已经不再被使用的对象清理掉,释放宝贵的内存资源。
垃圾对象识别
JVM 通过一种可达性分析算法进行垃圾对象的识别,具体过程是:从线程栈帧中的局部变量,或者是方法区的静态变量出发,将这些变量引用的对象进行标记,然后看这些被 标记的对象是否引用了其他对象,继续进行标记,所有被标记过的对象都是被使用的对象,而那些没有被标记的对象就是可回收的垃圾对象了。
垃圾对象回收
进行完标记以后,JVM 就会对垃圾对象占用的内存进行回收,回收主要有三种方法
清理:将垃圾对象占据的内存清理掉,其实 JVM 并不会真的将这些垃圾内存进行清理,而是将这些垃圾对象占用的内存空间标记为空闲,记录在一个空闲列表里,当应用程序需要创建新对象的时候,就从空闲列表中找一段空闲内存分配给这个新对象。
压缩:从堆空间的头部开始,将存活的对象拷贝放在一段连续的内存空间中,那么其余的空间就是连续的空闲空间。
复制:将堆空间分成两部分,只在其中一部分创建对象,当这个部分空间用完的时候,将标记过的可用对象复制到另一个空间中。
在使用中,更多的是使用压缩或复制的方法进行垃圾回收。
JVM 分代垃圾回收策略
分代垃圾回收是把内存空间分成两个代,一个叫做新生代,一个叫做老年代。
在实际中发现 Java 中的对象大部分的生存时间是很短暂的,比如在方法中 new 一个新对象,方法执行完后退出方法的时候,栈帧出栈,引用被销毁了,对象就失去引用了,变成垃圾对象,就可以回收了,仅仅是一个方法内部的生存时间。针对这些短暂生存时间的对象,可否放在一个较小的区域里去,并快速的对较小区域里的对象进行回收,这样回收的范围比较小,扫描的对象少,回收速度更快。
基于这种思路把 Java 的堆内存空间分成了两块,一块是新生代,一块是老年代,对象创建的时候在新生代中创建,回收的时候先在新生代中回收,如果经过一段时间,对象还没有被回收,说明这个对象的生命周期比较长,再把它复制到老年代。
新生代中分了三个区域,一个 Eden 区,一个 From 区,一个 To 区。
对象刚刚创建的时候是在 Eden 区创建,当 Eden 区满的时候,就要进行垃圾回收了,Eden 区的大部分对象在回收的时候都已经变成垃圾对象了,我们就可以把这些垃圾对象回收掉。
具体的回收过程用的是复制算法,我们先进行对象标记,把通过标记在用的对象拷贝到 From 区,剩下的就是一块完全空白的 Eden 区,垃圾回收做完后,程序代码执行时还会继续在 Eden 区创建对象,Eden 区满后,会再次进行对象标记并把可用对象拷贝到 From 区,当 From 区满后,会把 From 区的对象拷贝到 To 区,这样 From 区也空了,可以继续和 Eden 区配合进行垃圾回收操作。在回收的时候,会同时遍历 Eden 区和 To 区的可用对象,并将其拷贝到 From 区,这样三个区域配合反复进行垃圾回收操作。
老年代就一个区,空间相对比新生代更大。
当进行几次回收操作以后,如何还有对象没有被回收,说明该对象生命周期会比较长,就不会在新生代进行来回拷贝了,会把这个对象拷贝到老年代。拷贝一段时间后老年代的对象也满了,会对老年代也进行一次垃圾回收。
垃圾回收过程
垃圾回收分为两种,一种是对新生代进行垃圾回收,一种是对老年代进行垃圾回收,当对老年代垃圾回收的时候,对新生代也要进行垃圾回收,所以对垃圾回收可以分成 Young GC(新生代的垃圾回收)和 Full GC(全量的垃圾回收,新生代和老年代一起垃圾回收)两种垃圾回收过程
JVM 垃圾回收器算法
具体在进行垃圾回收,在标记垃圾对象以及回收垃圾对象空间的时候,垃圾回收程序(垃圾回收器)在 JVM 不同的版本中有很多中,分别采用不同的算法实现
典型的有串行回收器、并行回收器、并发回收器、G1 回收器
最早的是串行回收器,应用程序多线程在执行,当空间不足,需要进行垃圾回收的时候,所有的线程都停止运行(叫做 stop the world),启动一个垃圾回收线程进行对象标记和垃圾空间的清理回收,垃圾回收完了以后再启动应用线程,由于垃圾回收线程只有一个,所以叫串行垃圾回收器。
并行回收器,当出现多核 CPU 以后,可以针对 CPU 的核数同时启动多个垃圾回收线程,并行的进行垃圾回收,依然采用 stop the world 方式,所有用户线程被终止。
并发回收器,采用 stop the world 方式,所有用户线程被终止,对用户线程影响比较大,影响响应时间,为了避免垃圾回收对用户线程的影响,提高用户线程的执行效率,减少响应时间,产生了并发垃圾回收器,把整个的垃圾回收过程分得更加详细,分成初始标记、并发标记、重标记和并发清理 4 个阶段,初始标记时需要 stop the world,时间会很短,然后进入并发标记阶段,垃圾回收线程和用户线程并发执行,不会对用户线程造成较大的影响,并发标记完了以后,由于在标记过程中又有新的对象产生,前面标记过得可能会不准确,所以需要重新进行标记,在这个过程中,为了保证不会有对象被遗漏,重标记的时候还需要 stop the world,只有垃圾回收线程进行标记,所有对象标记完后,然后进行清理在清理的时候,清理的垃圾回收线程和用户线程也是并发的执行的,所以整个过程中,大部分的时间垃圾回收线程和用户线程是并发执行的,stop the world 的时间就会比较少,对于用户线程的影响比较小,对响应时间的影响也比较小。对 web 应用请求,以前主要使用 CMS 并发回收器。
G1 回收器,对比较新的版本,更多的使用 G1 垃圾回收器,它把整个的内存空间分成了更小的区域,默认情况下分成 2000 个区域,每个区域管理较小的内存空间,垃圾回收标记处理速度更快,这些小块也有自己的角色,包括 Eden 区,From 区 To 区(Survivor),老年区(Old),大内存区(Humongous),进行垃圾回收的时候针对具体的区域进行回收,动态的去调整回收的过程,对用户程序影响也比较小,占用的资源也比较小,可以管理的内存空间更大,这里主要使用的是 Max 最大的 GC 暂停时间去控制,G1 垃圾回收器会根据期望的指定时间,动态的调整回收的策略,以达到期望的 stop the world 的时间。
二、设计一个秒杀系统,主要的挑战和问题有哪些?核心的架构方案或者思路有哪些?
技术挑战
瞬间高并发
8000 并发:预估秒杀在线人数可达 8000 人 。
风险:带宽耗尽。
服务器:崩溃,可以理解成自己给自己准备的 D.D.O.S 攻击。
秒杀器
第一种:秒杀前不断刷新秒杀页面,直到秒杀开始,抢着下单。
第二种:跳过秒杀页面,直接进入下单页面,下单。
架构方案设计原则
静态化
采用 JS 自动更新技术将动态页面转化为静态页面
并发控制,防秒杀器
设置阀门,只放最前面的一部分人进入秒杀系统
简化流程
砍掉不重要的分支流程,如下单页面的所有数据库查询
以下单成功作为秒杀成功标志。支付流程只要在 1 天内完成即可。
前端优化
采用 YSLOW 原则提升页面响应速度
版权声明: 本文为 InfoQ 作者【piercebn】的原创文章。
原文链接:【http://xie.infoq.cn/article/62eb87a3b6c9d8e235c6863ad】。文章转载请联系作者。
评论