相信很多人对于性能调优都不陌生,为了获得更好的系统性能,或者是为了满足不断增加的业务需求。都需要用到我们的性能调优。所以性能优化在面试中出现的频率特别高,这篇文章我主要给大家整理了大厂里面关于jvm和性能调优用到的一些核心技术知识点。
注意,注意,整个对于jvm和性能调优的知识点整理会很详细,文中所有的细节并不会全展示给大家,大家觉得对自己有帮助的朋友可以点击此处获取
目录:
- JVM 内存区域划分
- JVM 执行子系统
- 垃圾回收器和内存分配策略
- 编写高效优雅 Java 程序
- 性能优化
JVM 内存区域划分
1.程序计数器(线程私有)
程序计数器(Program Counter Register),也有称作为 PC 寄存器。保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),当 CPU 需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加 1 或者根据转移指针得到下一条指令的地址,如此循环,直至执行完所有的指令。也就是说是用来指示执行哪条指令的。
由于在 JVM 中,多线程是通过线程轮流切换来获得 CPU 执行时间的,因此,在任一具体时刻,一个 CPU 的内核只会执行一条线程中的指令,因此,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。
在 JVM 规范中规定,如果线程执行的是非 native 方法,则程序计数器中保存的是当前需要执行的指令的地址;如果线程执行的是 native 方法,则程序计数器中的值是 undefined。
由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。
异常情况:不存在
2.Java 栈(线程私有)
Java 栈也称作虚拟机栈(Java Vitual Machine Stack)
Java 栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表、操作数栈、指向当前方法所属的类的运行时常量池的引用、方法返回地址、额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。因此可知,线程当前执行的方法所对应的栈帧必定位于 Java 栈的顶部。
局部变量表,用来存储方法中的局部变量(包括在方法中声明的非静态变量以及函数形参)。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。局部变量表的大小在编译器就可以确定其大小了,因此在程序执行期间局部变量表的大小是不会改变的。
3.本地方法栈(线程私有)
2.可扩展深度大于能够申请的内存:OutOfMemoryError
4.堆(线程共享)
5.方法区(线程共享)
6.直接内存(线程共享)
JVM 执行子系统
1.Class 类文件结构
2.字节码指令
3.类加载机制
4.类加载器
5.Tomcat 类加载机制
6.方法调用详解
三.垃圾回收器和内存分配策略
1.Java 中是值传递还是引用传递?
但是传引用的错觉是如何造成的呢?在运行栈中,基本类型和引用的处理是一样的,都是传值,所以,如果是传引用的方法调用,也同时可以理解为“传引用值”的传值调用,即引用的处理跟基本类型是完全一样的。但是当进入被调用方法时,被传递的这个引用的值,被程序解释(或者查找)到堆中的对象,这个时候才对应到真正的对象。如果此时进行修改,修改的是引用对应的对象,而不是引用本身,即:修改的是堆中的数据。所以这个修改是可以保持的了。
对象,从某种意义上说,是由基本类型组成的。可以把一个对象看作为一棵树,对象的属性如果还是对象,则还是一颗树(即非叶子节点),基本类型则为树的叶子节点。程序参数传递时,被传递的值本身都是不能进行修改的,但是,如果这个值是一个非叶子节点(即一个对象引用),则可以修改这个节点下面的所有内容。
2.引用类型
对象引用类型分为强引用、软引用、弱引用和虚引用。
强引用:就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收时需要严
格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收
软引用:软引用一般被做为缓存来使用。与强引用的区别是,软引用在垃圾回收时,虚拟机
会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张,则虚拟
机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。换句话说,虚拟
机在发生 OutOfMemory 时,肯定是没有软引用存在的。
弱引用:弱引用与软引用类似,都是作为缓存来使用。但与软引用不同,弱引用在进行垃圾
回收时,是一定会被回收掉的,因此其生命周期只存在于一个垃圾回收周期内。
强引用不用说,我们系统一般在使用时都是用的强引用。而“软引用”和“弱引用”比较少见。
他们一般被作为缓存使用,而且一般是在内存大小比较受限的情况下做为缓存。因为如果内
存足够大的话,可以直接使用强引用作为缓存即可,同时可控性更高。因而,他们常见的是
被使用在桌面应用系统的缓存。
3.基本垃圾回收算法
4.分代处理垃圾
5.JAVA 中垃圾回收 GC 的类型
由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC 有两种类型:ScavengeGC 和 Full GC。
Scavenge GC: 一般情况下,当新对象生成,并且在 Eden 申请空间失败时,就会触发Scavenge GC,对 Eden 区域进行 GC,清除非存活对象,并且把尚且存活的对象移动到Survivor 区。然后整理 Survivor 的两个区。这种方式的 GC 是对年轻代的 Eden 区进行,不会影响到年老代。因为大部分对象都是从 Eden 区开始的,同时 Eden 区不会分配的很大,所以 Eden 区的 GC 会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden 去能尽快空闲出来。
Full GC: 对整个堆进行整理,包括 Young、Tenured 和 Perm。Full GC 因为需要对整个对进行回收,所以比 Scavenge GC 要慢,因此应该尽可能减少 Full GC 的次数。在对 JVM调优的过程中,很大一部分工作就是对于 FullGC 的调节。有如下原因可能导致 Full GC:· 年老代(Tenured)被写满、持久代(Perm)被写满、System.gc()被显示调用 、上一次 GC之后 Heap 的各域分配策略动态变化。
四、编写高效优雅 Java 程序
1.面向对象
2.方法
3.通用程序设计
3.1 用枚举代替 int 常量
声明的一个枚举本质就是一个类,每个具体的枚举值就是这个枚举类的实例。枚举更多作用,看代码。
3.2 将局部变量的作用域最小化
1. 在第一次使用的地方进行声明
2. 局部变量都是要自行初始化,初始化条件不满足,就不要声明
最小化的好处,减小局部变量表的大小,提示性能;同时避免局部变量过早声明导致不正确的使用。
3.3 精确计算,避免使用 float 和 double
可以使用 int 或者 long 以及 BigDecimal
3.4 当心字符串连接的性能
参考代码
com.xiangxue.ch04.StringUnion15.Test。
在存在大量字符串拼接或者大型字符串拼接的时候,尽量使用 StringBuilder 和 StringBuffer
五、性能优化
1.常用的性能评价/测试指标
2.常用的性能优化手段
3 应用服务性能优化
总结:
jvm性能调优一直是大厂面试的一个重点:上面的jvm性能调优的话没有将所有的知识点细节整理出来给大家,所有的核心技术知识点都整理到了PDF文档上面,出去jvm性能调优的核心知识点整理外,小编还整理了关于有java集合,微服务,kafka,设计模式,等核心知识点的整理。
更多核心知识点截图: