JVM性能调优与底层原理分析(学习笔记)
Posted 醉酒的小男人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM性能调优与底层原理分析(学习笔记)相关的知识,希望对你有一定的参考价值。
JDK的结构体系
首先要搞清楚JRE是Java程序运行环境,而JRE最基础的就是JVM,JDK是包含了JRE和其他的一些基本类库。
JVM整体架构
当你在不同系统下载安装JDK时,Oracle会让你选择系统的版本,这就是JVM从软件层面屏蔽了不同操作系统在底层硬件与指令上的区别
JVM内存模型
类装载器子系统
就相当于是快递员,它将.class文件进行装载、验证(每个class文件在开头有特定的文件标识:cafe babe)、准备、解析、初始化,然后将创建好的对象送到运行时数据区(内存中)。
运行时数据区(内存模型)
只有把这块搞懂了才能进行调优
栈(线程)
栈是线程独有的,每个线程都会单独分配一块栈内存空间,它会存放局部变量等。
栈帧:
线程中一个方法对应一块栈桢区域,当方法调用完之后会将栈桢区域进行释放。栈的结构与数据结构中的栈类似,元素都是先进后出(FILO),方法的嵌套调用与栈这种数据结构是非常契合的。
使用javap命令将字节码文件进行反汇编为JVM指令码文件,再对应Orcale官方的指令码文件 Oracle官网地址
- 局部变量表:用来存放局部变量,对象形式的局部变量存储的是指向堆内 存对象的内存地址
- 操作数栈:用来存放代码运行过程中临时的操作数
- 动态链接:
- 方法出口:存放方法在调用者的位置,方便方法执行完成后回到调用者对应的位置继续向下执行其他代码。
程序计数器
线程独有,用来存储当前线程正在运行的那行代码的位置
当CPU资源被抢占,线程挂起,得到CPU的资源后的线程之所以可以继续运行是因为有程序计数器记录了代码运行的位置,线程中每执行一行代码字节码执行引擎都会动态修改程序计数器的值。
方法区(元空间)
线程共享,JDK1.8之前叫永久代,JDK1.8之后叫元空间
最大的区别是:永久代使用的是JVM的堆内存,元空间并不在虚拟机中而是使用本机物理内存。
主要存放:常量,静态变量,类信息
字节码文件通过类加载器将类的元素信息放到方法区
静态变量如果是对象的话,也存储的是对象在堆内存中的地址
本地方法栈
线程独有,存储本地方法
堆
线程共享
堆内存占比:
- 年轻代:1/3
- 伊甸园区:8/10
- Survivor区:2/10
- 老年代:2/3
GC 分析
- minor gc: 当伊甸园区放满对象之后就会触发 minor gc,通过复制算法会将存活的对象放到Survivor区,通过每次触发minor gc对象在Survivor区来回复制会增加分代年龄,当分代年龄达到15次,会将对象挪到老年代
- 可达性分析算法:将GC Roots对象作为起点,从这些节点开始向下搜索引用对象,找到的对象都标记为 非垃圾对象,其余未标记的都是垃圾对象
GC Roots根节点:线程本地变量、静态变量、本地方法栈的变量等等
- 对象头
- 监控工具:jvisualvm
- full gc:当老年代的对象存储满了之后会触发,会回收整个堆和方法区
调优
- STW :Stop-The-World ,停止掉用户的线程,当字节码执行引擎执行minor gc或者是full gc时会stw,调优的目的就是减少stw
实战调优
- 估算对象的大小,可以参考Java中变量大小,假定这个业务中订单对象为1kb
- 估算每秒大概产生多少订单,假定这个业务每秒产生300单,就是每秒产生300个订单对象,也就是300kb
- 下单还可能涉及其他对象,将每秒产生的对象放大20倍,就是(20*300)kb/s,每秒产生6M对象
- 同时还可能产生订单查询等其他操作,再放大10倍,就是(2030010)kb/s,每秒产生60M对象
- 估算多久会发生minor gc,此时就会产生STW
- 当Survivor区的对象占比超过Sucvivor区容量的50%的时候,对象会直接放到老年代(对象动态年龄判断)
- 判断什么时候会发生full gc
- 调整堆内存中的参数分配,使其尽量不发生gc
调优后的变化
要对业务中类的模型分析清楚,了解JVM分配机制
以上是关于JVM性能调优与底层原理分析(学习笔记)的主要内容,如果未能解决你的问题,请参考以下文章