写点什么

架构师训练营第九周 - 总结

发布于: 2020 年 08 月 05 日
架构师训练营第九周-总结

1.JVM虚拟机原理与编程优化

2.秒杀

JVM虚拟机原理与编程优化

JVM组成结构

java是一种跨平台的语言,JVM屏蔽了底层系统的不同,为Java字节码文件构造了一个统一的运行环境。

  • 类加载器

  • 运行期数据区

  • 方法区

  • Java栈

  • 程序计数寄存器

  • 执行引擎

Java字节码文件

Java在不同操作系统,不同硬件上运行的基础

Java虚拟机的指令码有200个左右,一个字节可以表示256种信息。Java通过一个字节表示了其所有指令信息

字节码文件前四个字节为cafe babe,是一个魔数,展现了Java字节码文件的特点,同时反应了Java发明者们的程序员幽默

字节码执行流程

  1. 方法调用

  2. 判断方法是否已被编译为机器码

  3. 编译为机器码的话,从Code Cache中取出编译后的机器码,直接调用

  4. 未编译为机器码,则方法调用计数器加1

  5. 判断计数是否超过阈值,未超过阈值,解释执行

  6. 方法调用计数器超过阈值,提交编译请求,由编译器异步编译,编译后,放入Code Cache中

  7. 本次调用依然使用解释执行

Java字节码文件编译过程

  1. Java源文件

  2. 词法分析

  3. 语法分析

  4. 语义分析

  5. 生成字节码

类加载器的双亲委托模型

  1. Bootstrap ClassLoader

  2. Platform ClassLoader

  3. Application ClassLoader

1-3,从父到子

类加载器在加载一个类之前,会请求父类代为加载,父类加载不了的,且允许子类加载的,当前类加载器才加载。

不同类加载器加载相同类到内存中,生成的Class对象是不同的,new出来的类实例也不可相互赋值

自定义类加载器

  1. 隔离加载类

同一个JVM中不同组件加载同一个类的不同版本

  1. 扩展加载源

从网络、数据库等处加载字节码

  1. 字节码加密

加载自定义的加密字节码,在ClassLoader中解密

运行期数据区

  1. 堆&堆栈

类实例和数组对象等是创建在堆中,所有线程共享,可能有并发问题。堆栈是线程独享的。

  1. 方法区&程序计数器

方法区中存储着加载的类信息,程序计数器记录着线程中方法运行到的代码行数。

  1. Java(线程)栈

方法内定义的基本类型变量,都会被每个运行这个方法的线程放入自己的栈中,线程的栈彼此隔离,所以这些变量一定是线程安全的。

线程工作内存&volatile

Java内存模型规定,在多线程情况下,线程操作主内存变量,需要通过线程独有的工作内存拷贝主内存变量副本来进行。

一个共享变量(类的成员变量,类的静态成员变量)被volatile修饰之后,那么他就具备了两层含义:

  • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其它线程来说是立即可见的

  • 禁止进行指令重排序

Java运行环境

  • Java编译环境

  1. 源代码

  2. Java编译器

  3. Java字节码

  • Java平台运行期环境

  1. 类装载器

  2. Java解释器与即时编译器

  3. 运行期系统

  4. 操作系统

  5. 硬件

JVM的垃圾回收

做垃圾回收需要解决的问题

  1. 什么样内存空间是垃圾

  2. 怎么处理垃圾

  3. 以什么样的方式确定和清理

  4. 什么时间清理

对于第一个问题,Java内存空间中,线程栈中的内存会随着栈帧入栈出栈而自动处理,方法区中的类信息占用内存小,也不需要处理。要处理的事堆中的内存。

怎么确定哪个是内存呢,通过可达性分析,判断哪些对象不再使用,进行清理。

可达性分析是通过栈帧中的对象引用,类信息中的静态成员对象引用,以及这些引用指向的对象的成员变量,逐级往下来找到所有可用的对象。则堆中没有在可达性中的对象都是可回收对象,可以进行垃圾回收

对于第二个问题,可以采用清理、压缩、复制的方式处理垃圾。

清理,就是标记对象内存空间为空闲,放入空闲列表,当应用程序需要创建新对象时,就从空闲空间列表中找一段空闲内存分配给这个新对象

压缩,从堆空间的头部开始,将活的空间拷贝到一段连续的内存空间中,那么其余的空间就是连续的空闲空间

复制,将堆空间分成两部分,只在其中一部分创建对象,当这个部分空间用完的时候,将标记过的可用对象复制到另一个空间中。

JVM分代垃圾回收

  1. 新生代

  2. Eden区

  3. From区

  4. To区

  5. 老年代

JVM垃圾回收期算法

回收期种类

  1. 串行回收器

一个线程标记与清除垃圾

  1. 并行回收器

多个线程标记与清除垃圾

  1. 并发回收器

初始化标记、并发标记、重标记、并发清理

  1. G1回收器

G1把内存分为很多块,这些块的类型有Eden、Survivor、Old、Humongous,每次选取一些块进行垃圾收集,尽量减少STW(Stop The World)的时间

以上四种垃圾回收期都会经历STW,区别是,尽量减少STW的时间,减小对应用程序的影响

JVM性能诊断工具

基本工具

  1. JPS

查看host上所有JAVA进程ID

  1. JSTAT

JVM提供的小工具,用以对JAVA应用程序的资源和性能进行实时的命令行监控,包括了对HeapSize和垃圾回收状况的监控

  1. JMAP

可以输出所有内存中对象的工具,也可以把堆以二进制文本的方式输出

集成工具

  1. JConsole

  2. JVisualVM

集成工具,可以监控cpu,内存,类,线程等资源

Java代码优化

合理并谨慎使用多线程

使用场景,I/O阻塞、多CPU并发

启动线程数=【任务执行时间/(任务执行时间-IO等待时间)】* CPU内存数

竞态条件与临界区

多个线程竞争同一资源,如果对资源的访问顺序敏感,就称存在竞态条件。

导致竞态条件发生的代码区被称为临界区

在临界区中使用适当的同步就可以避免竞态条件

Java线程安全

允许被多个线程安全执行的代码称作线程安全的代码

  1. 局部变量

基本类型的局部变量,存储在栈中,是线程安全的

  1. 局部对象引用

局部对象引用,引用不逃逸出对象,则是线程安全的

  1. 对象成员

对象成员存储在堆中,可以被多线程访问,不是线程安全的

ThreadLocal

ThreadLocal中存储的值,与线程相关,每个线程取出的都是每个线程自己放入的值,那么是怎么做到的呢?

首先,要明白一点,Thread也是一个对象,那么,其,也是可以有成员变量的,Thread有一个成员变量threadLocals,类型是ThreadLocalMap。每次threadLocal调用get时,会去thread中查找这个值,存储的key是这个ThreadLocal对象引用,值是在这个线程中设置的value。不同的线程,有不同的value。

这个设计很精巧,缺点是存储的对象不删除的话,会造成Thread对象中的threadLocals成员变量指向的对象中存储的值得不到释放,会越积越多,造成内存泄漏。

Java内存泄漏

Java内存泄露是由于开发人员的错误引起的

如果程序保存对永远不再使用的对象的引用,这些对象将会占用并耗尽内存

  • 长生命周期对象

  • 静态容器

  • 缓存

合理使用线程池和对象池
  • 使用线程和对象资源,避免在程序的生命周期中,大量创建和删除对象

  • 池管理算法(记录哪些对象是空闲的,哪些是正在使用的)

  • 对象内容清除(ThreadLocal的清空)

使用合适的JDK容器类
  1. LinkedList和ArrayList

  2. HashMap

  3. ConcurrentHashMap

缩短对象生命周期,加快垃圾回收
  1. 减少对象驻留内存的时间

  2. 在使用时,创建对象,用完释放

  3. 创建对象的步骤(静态代码段->静态变量->父类构造器->子类构造器)

使用I/O buffer及NIO
  • 延迟写与提前读策略

  • 异步无阻塞I/O通信

优先使用组合代替继承
  • 减少对象耦合

  • 避免太深的继承层次带来的对象创建性能损失

合理使用单例模式
  • 无状态对象

  • 线程安全

虚拟化所有层次
  • 计算机的任何问题都可以通过间接层解决

  • 一致性hash算法的虚拟化实现

  • 面向接口编程

  • 7层网络协议



秒杀系统

秒杀活动

秒杀是指在很短的时间内,多人抢购一件或几件商品,通常在一秒内抢购完成,所以称为秒杀,通常表示极短时间内活动时间很短。

秒杀系统特点

  1. 高并发

  2. 到点(秒杀时间点)才可以下单

秒杀面对的问题

  1. 高并发下的系统处理压力(应用、数据库)

  2. 高并发下的系统带宽压力

  3. 对原有业务系统的影响

解决问题的思路

  1. 秒杀系统单独立为一个系统,域名和原业务系统域名最好也做一个区分

  2. 秒杀申请独立的带宽,可以租用CDN

  3. 动态页面静态化,放在CDN中

  4. 业务信息,如商品,等存放在缓存中,加快读效率

  5. 在尽量保证公平公正的前提下,设置阈值,按照访问服务器层级,逐级减小后续服务器请求数

  6. 选用I/O处理优秀的应用服务器

  7. 页面尽量简洁

  8. 到点生成动态访问URL,存入js文件中,推送到js文件服务器,用户秒杀时,读取到最新的js文件,页面秒杀按钮可点击,服务器可受理。动态生成的URL,要无规律,不可猜出,这样才能保证秒杀时这个URL大家公平访问,秒杀前都不可访问,保证公平

  9. 秒杀中出现任何问题,服务端返回秒杀结束页面,减少产生纠纷

  10. 秒杀时,只关注必要流程,如下单。下单后,付款流程可在一定时间完成,如一小时内。



用户头像

喜欢简洁干净的代码 2018.05.04 加入

使用技术,实现业务。思考业务,创新技术。

评论

发布
暂无评论
架构师训练营第九周-总结