JVM 垃圾回收原理与秒杀系统
一、请简述 JVM 垃圾回收原理。
一、JVM 垃圾回收原理
从两个方面简述,jvm内存模型和jvm回收算法,前面是问题的根,后面是解决问题的方案。
1、JVM内存模型
pc寄存器:当前线程所执行的字节码的行号指示器
虚拟机栈:用于存储局部变量表、操作栈、动态链接、方法出口等信息
本地方法栈:局部变量
堆:class对象、数组,所有线程共享,不是所有的对象都在堆上分配,比如JIT优化-逃逸分析
方法区:运行时数据:静态变量、静态方法、常量池、类的代码
Java虚拟机运行时数据区域主要包含了PC寄存器(程序计数器)、Java虚拟机栈、本地方法栈、Java堆、方法区以及运行时常量池。
在JVM运行时内存区域中,PC寄存器、虚拟机栈和本地方法栈是线程独享的。
而Java堆、方法区是线程共享的。但是值得注意的是,Java堆其实还未每一个线程单独分配了一块TLAB空间,这部分空间在分配时是线程独享的,在使用时是线程共享的。
JVM中存在着大量的声明短暂的对象,还有一些生命周期比较长的对象。为了对他们采用不同的收集策略,采用了分代收集算法,所以HotSpot虚拟机把的根据对象的年龄不同,把堆分位新生代、老年代和永久代(8之后为元空间)。
对于一个普通的Java对象的创建,大致过程如下:
1、虚拟机遇到new指令,到常量池定位到这个类的符号引用。
2、检查符号引用代表的类是否被加载、解析、初始化过。
3、虚拟机为对象分配内存。
4、虚拟机将分配到的内存空间都初始化为零值。
5、虚拟机对对象进行必要的设置。
6、执行方法,成员变量进行初始化。
2、垃圾回收算法
垃圾回收的依据
对象存活判断:引用计数(根据对象的引用计数,新增+1,释放-1,0可以回收,无法解决对象互相循环引用的问题)和可达性分析(GC roots向下搜索,查看引用链,当一个对象到GC roots没有引用链则不可用,为不可达对象)
GC算法:标记 -清除算法、复制算法、标记-压缩算法、分代收集算法
垃圾回收器
垃圾回收器可以分为四类,前三种都基于标记-清除(复制)算法:
标记:在需要回收的内存空间中,扫描并标记哪些区块是在使用的,哪些已经失效。视需要扫描的内存空间大小而定,这个过程可能相对会比较耗时。
清除:将被标记为未使用的内存区块对应的对象引用删除。
压缩:如果使用中的内存区块碎片化,需要把它们挪动到一起组成连续空间,以提高内存空间分配效率。相当于把使用中的内存区块分布区域“压缩”到了小块连续区域。
分代回收
标记内存使用情况需要扫描整个内存空间。如果内存空间很大,这个过程会相当耗时。另一方面,大部分对象的生命周期都非常短,创建后不久就失效了。基于这两点,JVM将内存空间又进一步按照对象存活时间划分成了代际区块。
并行算法是用多线程进行垃圾回收,回收期间会暂停程序的执行,而并发算法,也是多线程回收,但期间不停止应用执行。所以,并发算法适用于交互性高的一些程序。经过观察,并发算法会减少年轻代的大小,其实就是使用了一个大的年老代,这反过来跟并行算法相比吞吐量相对较低。
垃圾回收动作何时执行?下面的流程进行说明
新创建的对象放在Eden区(年轻代)
当年轻代内存满时,会引发一次普通GC(minorGC),该GC仅回收年轻代,仍被引用的对象被复制到S0,未被引用的对象留在Eden区,随后被全部清空。需要强调的时,年轻代满是指Eden代满,Survivor满不会引发GC。当下一次minor GC触发时,Eden区及S0区的有被引用对象都被复制到S1区,随后Eden和S0被清空释放。当Eden区再次触发minorGC时,由于S1已经在上次GC后装载了幸存对象,而S0被清空。所有这次将反过来,将Eden区和S1的有引用对象复制到S0,然后清空Eden和S1区。在经历了数次这样的GC过程后,某些幸存对象已经足够成熟,将被复制到老年区,而还不够成熟的对象将继续在S0和S1之间复制,经受历练。
当年老代满时会引发Full GC,Full GC将会同时回收年轻代、年老代
当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载
垃圾回收器
根据这几个算法在不同的阶段实现的垃圾回收器如下:
串行回收器:早期单核 用单线程效率最高
并行回收器:多核,若服务器cpu内核低于3,实际效果与串行回收器没有差别。
并发回收器CMS:并发标记清理,获取最短回收停顿时间为目标的收集器,可能牺牲一定的吞吐量
G1回收器:针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征,从Java7开始,作为CMS的替代品被引入。它是并行的,多线程,低暂停时间,增量压缩式的回收器。
JVM会根据机器的硬件配置对每个内存代选择适合的回收算法,比如,如果机器多于1个核,会对年轻代选择并行算法,关于选择细节请参考JVM调优文档。
OOM异常
另一个问题是,何时会抛出OutOfMemoryException,并不是内存被耗空的时候才抛出
JVM98%的时间都花费在内存回收
每次回收的内存小于2%
满足这两个条件将触发OutOfMemoryException,这将会留给系统一个微小的间隙以做一些Down之前的操作,比如手动打印Heap Dump。
Jvm参数优化目标:
GC的时间足够的小
GC的次数足够的少
发生Full GC的周期足够的长
为了达到上面的目的,可以优化的点:
1、减少使用全局变量和大对象;
2、调整新生代的大小到最合适;
3、设置老年代的大小为最合适;
4、选择合适的GC收集器;
3、调优命令
jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
jmap,JVM Memory Map命令用于生成heap dump文件
jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
jstack,用于生成java虚拟机当前时刻的线程快照。
jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。
二. 设计一个秒杀系统, 主要的挑战和问题有哪些?
1、秒杀场景特点
定时开始,秒杀时大量用户会在同一时间,抢购同一商品,网站瞬时流量激增。
库存有限,秒杀下单数量远远大于库存数量,只有少部分用户能够秒杀成功。
操作可靠,秒杀业务流程比较简单,一般就是下订单减库存。库存就是用户争夺的“资源”,实际被消费的“资源”不能超过计划要售出的“资源”,也就是不能被“超卖”。
2、秒杀的场景特点带来的挑战和问题
由于短时间内的高并发,会产生大量的请求,对网络和服务资源都有竞争性,不能和其它业务糅合到一起,也不是改改原来的就可以了,需要由顶而下做资源隔离(重新搭建秒杀系统)。需要对某一点都要进行资源扩容和优化,同时进一步做限流。
宽带资源
服务器资源(style、图片、静态资源等)
交易服务器
3、解决问题的步骤和方案
通过商业需求,分析并发量等
分析目前的性能现状,需要做什么
系统隔离:业务隔离、技术隔离、数据库隔离
性能压测:任何系统没有测试都是没有保证的。
突发状况响应:对一些不重要的功能或系统熔断降级等,为秒杀让路。
https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
评论