2019年度总结
前言
作为Java程序员,你有没有被JVM伤害过?面试的时候是否碰到过对JVM的灵魂拷问?
一、JVM 内存区域划分
1.程序计数器(线程私有)
程序计数器(Program Counter Register),也有称作为 PC 寄存器。保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),当 CPU 需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加 1 或者根据转移指针得到下一条指令的地址,如此循环,直至执行完所有的指令。也就是说是用来指示执行哪条指令的。
由于在 JVM 中,多线程是通过线程轮流切换来获得 CPU 执行时间的,因此,在任一具体时刻,一个 CPU 的内核只会执行一条线程中的指令,因此,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。
在 JVM 规范中规定,如果线程执行的是非 native 方法,则程序计数器中保存的是当前需要执行的指令的地址;如果线程执行的是 native 方法,则程序计数器中的值是 undefined。
由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。
异常情况:
不存在
2.Java 栈(线程私有)
3.本地方法栈(线程私有)
4.堆(线程共享)
5.方法区(线程共享)
6.直接内存(线程共享)
二、JVM 执行子系统
1.Class 类文件结构
1.1 Java 跨平台的基础
各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(ByteCode)是构成平台无关性的基石,也是语言无关性的基础。Java 虚拟机不和包括 Java 在内的任何语言绑定,它只与“Class 文件”这种特定的二进制文件格式所关联,Class 文件中包含了 Java虚拟机指令集和符号表以及若干其他辅助信息。
1.2 Class 类的本质
任何一个 Class 文件都对应着唯一一个类或接口的定义信息,但反过来说,Class 文件实际上它并不一定以磁盘文件的形式存在。Class 文件是一组以 8 位字节为基础单位的二进制流。
1.3 Class 文件格式
各个数据项目严格按照顺序紧凑地排列在 Class 文件之中,中间没有添加任何分隔符,这使得整个 Class 文件中存储的内容几乎全部是程序运行的必要数据,没有空隙存在。Class 文件格式采用一种类似于 C 语言结构体的伪结构来存储数据,这种伪结构中只有两种数据类型:无符号数和表。
无符号数属于基本的数据类型,以 u1、u2、u4、u8 来分别代表 1 个字节、2 个字节、4 个字节和 8 个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8编码构成字符串值。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个 Class 文件本质上就是一张表。
2.字节码指令
2.1 加载和存储指令
2.2 运算或算术指令
2.3 类型转换指令
2.4 创建类实例的指令
2.5 创建数组的指令
2.6 访问字段指令
2.7 数组存取相关指令
2.8 检查类实例类型的指令
2.9 操作数栈管理指令
2.10 控制转移指令
2.11 方法调用指令
2.12 方法返回指令
2.13 异常处理指令
2.14 同步指令
3.类加载机制
4.类加载器
4.1 系统的类加载器
4.2 双亲委派模型
5.Tomcat 类加载机制
6.方法调用详解
6.1 解析
6.2 静态分派
6.3 动态分派
6.4 基于栈的字节码解释执行引擎
三.垃圾回收器和内存分配策略
1.Java 中是值传递还是引用传递?
2.引用类型
3.基本垃圾回收算法
3.1.1 引用计数(Reference Counting):
比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为 0 的对象。此算法最致命的是无法处理循环引用的问题。
3.1.2 可达性分析清理
标记-清除(Mark-Sweep):此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。
复制(Copying): 此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,
搭建本地测试es集群
不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。
标记-整理(Mark-Compact):此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,清除标记对象,并未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
3.1 按照基本回收策略分
3.2 按分区对待的方式分
3.3 按系统线程分
4.分代处理垃圾
5.JAVA 中垃圾回收 GC 的类型
四、编写高效优雅 Java 程序
1.面向对象
1.1 构造器参数太多怎么办?
用 builder 模式,用在
(1)5 个或者 5 个以上的成员变量
(2)参数不多,但是在未来,参数会增加
Builder 模式:
属于对象的创建模式,一般有
(1)抽象建造者:一般来说是个接口,包含
1)建造方法,建造部件的方法(不止一个)
2)返回产品的方法
(2) 具体建造者
(3) 导演者,调用具体的建造者,创建产品对象
(4)产品,需要建造的复杂对象
对于客户端,创建导演者和具体建造者,并把具体建造者交给导演者,然后由客户端通知导演者操纵建造者进行产品的创建。
在实际的应用过程中,有时会省略抽象建造者和导演者。
1.2 不需要实例化的类应该构造器私有
1.3 不要创建不必要的对象
1.4 避免使用终结方法
1.5 使类和成员的可访问性最小化
1.6 使可变性最小化
1.7 复合优先于继承
1.8 接口优于抽象类
2.方法
2.1 可变参数要谨慎使用
2.2 返回零长度的数组或集合,不要返回 null
2.3 优先使用标准的异常
3.通用程序设计
五、性能优化
一个 web 应用不是一个孤立的个体,它是一个系统的部分,系统中的每一部分都会影响整
个系统的性能
1.常用的性能评价/测试指标
1.1 响应时间
提交请求和返回该请求的响应之间使用的时间,一般比较关注平均响应时间。
常用操作的响应时间列表:
1.2 并发数
同一时刻,对服务器有实际交互的请求数。
和网站在线用户数的关联:1000 个同时在线用户数,可以估计并发数在 5%到 15%之间,也就是同时并发数在 50~150 之间。
1.3 吞吐量
对单位时间内完成的工作量(请求)的量度
1.4 关系
系统吞吐量和系统并发数以及响应时间的关系:
理解为高速公路的通行状况:
吞吐量是每天通过收费站的车辆数目(可以换算成收费站收取的高速费),并发数是高速公路上的正在行驶的车辆数目,响应时间是车速。车辆很少时,车速很快。但是收到的高速费也相应较少;
随着高速公路上车辆数目的增多,车速略受影响,但是收到的高速费增加很快;
随着车辆的继续增加,车速变得越来越慢,高速公路越来越堵,收费不增反降;
如果车流量继续增加,超过某个极限后,任务偶然因素都会导致高速全部瘫痪,车走不动,当然后也收不着,而高速公路成了停车场(资源耗尽)。
2.常用的性能优化手段
2.1 避免过早优化
2.2 进行系统性能测试
2.3 寻找系统瓶颈,分而治之,逐步优化
2.4 前端优化常用手段
3 应用服务性能优化
3.1 缓存
3.1.1 缓存的基本原理和本质
3.1.2 合理使用缓冲的准则
3.1.3 分布式缓存与一致性哈希
3.2 异步
3.2.1 同步和异步,阻塞和非阻塞
3.2.2 常见异步的手段
3.3 集群
3.4 应用相关
3.4.1 代码级别
3.4.2 并发编程
3.4.3 资源的复用
3.4.4 JVM
3.4.5 GC 调优
3.4.6 调优实战
3.4.7 存储性能优化
上面的这些问题只是给大家一个借鉴作用,最主要的是给自己增加知识的储备,有备无患。
关于JVM与性能调优总结了将近50页pdf文档,欢迎关注我的公种浩:程序员追风,获取这些整理的资料!
希望能帮助到你面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
最后
欢迎大家一起交流,喜欢文章记得关注我点个赞哟,感谢支持!
在 Ubuntu 上安装 K8S教程