2021JVM
JVM
一、class加载过程
加载loading:将class文件加载到内存,加载到方法区,在堆中生成一个class对象
验证verification:验证是否符合class文件规范,验证是否符合java语言规范
准备preparation:为静态变量分配内存,设置默认值
解析resolution:将常量池内的符号引用转为直接引用(如物理内存地址指针)
初始化initializing:为变量赋真正的值,执行静态语句块,如果发现父类没初始化触发父类初始化
二、类加载器
Bootstrap 最顶层的加载器,加载JDK核心类,rt.jar、String用的C++实现,获取classload为null
Extension 加载jre扩展包、ext下的包
Application 加载我们写的类,加载classpath的类
CustomClassLoad 自定义类加载器
三、双亲委派模型
先找自定义类加载器是否已经加载,如果没有让上一层查找是否已经加载,找到顶层都没找到,查看顶层是否可以加载,如果可以,加载返回,如果不可以,向下反馈去加载,直到找到可以加载的类加载器,都没找到抛出classNotFind异常,
使用双亲委派模型主要是考虑安全问题,都能加载java内部的类,如String就会不安全
类加载器:可以实现热部署、动态代理
四、缓存行
读入CPU缓存时,不是读什么放什么,而是把之后的64位都放入缓存,称缓存行。
可以让两个变量处于不同缓存行,防止互相更新缓存导致效率变低
五、对象的创建过程
首先检查这个类是否已被加载,没有执行相应的类加载过程,加载后为对象分配内存,如果堆是规整的,则将指针向空闲部分移动对象的大小,分配移动的空间(指针碰撞),如果堆不是规整的,则需要有一个空闲列表,记录哪些内存是可用的(空闲列表),serial、ParNew垃圾收集器是使用的指针碰撞,CMS使用的空闲列表;分配时多线程需要同步处理,也可以将堆根据不同线程分成不同块,分配时先在线程对应块中分配,分配不下再使用同步(TLAB),可通过-XX:+UserTLAB参数设定;赋初始值;然后将对象头信息填充;赋默认值;执行init方法
六、对象头的信息
对象hash码、对象分代年龄、锁信息、是否偏向锁
七、对象的内存占用情况
对象头 8字节
classPointer类指针,指向class类,默认压缩4个字节,不压缩8个字节
实例数据
padding对齐,需要对齐为8的倍数
八、对象访问定位
T t = new T
两种方式:
- 句柄池,t指向句柄,句柄指向T与Class对象,对象移动可以只移动句柄指针
- 直接指针,直接指向T,T指向class对象,访问速度快
九、java运行时数据区域
PC程序计数器:存放指令位置
heap堆:
JVM stacks虚拟机栈:一个方法一个栈帧,栈帧里有:局部变量表、操作数栈、动态链接(A方法调B方法,存B方法的位置)、返回地址(A调用了B,B的返回值地址)
native stacks 本地方法栈:
method area 方法区:
1.8之前是perm Space 永久区,字符串常量在此区,固定内存
1.8之后是meta space 元数据空间,字符串常量在堆,可无上限内存
十、怎样找到垃圾去回收
引用计数法:循环引用不会被收集
可达性算法:是否与GC root相连,静态变量、常量池、class对象
十、清除算法
标记清除:适合存活对象比较多的地方,会产生碎片
复制算法:适合存活对象比较少的情况,会造成空间浪费
编辑整理:将存活的对象移动到一起,需要移动对象,效率低,不会产生碎片、不会空间浪费
十一、分代
新生代:eden(伊甸区),survivor(幸存区),survivor(幸存区) 8:1:1
老年代:
大对象直接分配到老年代,防止新生代复制大对象
垃圾回收一次后存活的放入survivor,多次回收后还存活放入老年区(默认15次)
动态年龄:survivor区中有某个年龄占用的空间大于一半,则大于该年龄的对象进入老年代
十二、垃圾收集器
单线程:serial年轻代、serial old 老年代(十几M) -XX:+UserSerialGC
只有一个线程GC,停顿时间长
多线程:Parallel年轻代、Parallel Old 老年代(几百M) -XX:+UserParallelGc
多个GC线程,但是GC 时也需要工作线程停顿
CMS真正的多线程:可以不停止其他工作线程,过程:
初始标记:找到根对象(单线程,标记对象较少)
并发标记:把其他连接根对象的标记(多线程)
重新标记:把并发标记中发生变动的重新标记(单线程)
并发清理:清理没有标记的
并发清理时可能产生新的垃圾(浮动垃圾),等下一次GC清理
CMS问题:使用标记清除,内存碎片,碎片太多会使用serial old清理,也会造成卡顿
可以降低CMS阈值 -XX:CMSInitiatingOccpancyFraction,勤CMS,默认68%
G1:将整个堆划分为多个大小相等的独立区域(Region),新生代和老年代不是物理隔离,都是一部分region集合;G1会跟踪各个Region里垃圾堆积的价值大小(回收所获得的空间大小以及回收时间的经验值),维护一个优先列表,优先回收价值最大的区域(garbage
-First),每个region里都维护一个Rset(rememberset),记录着其他区域的对象到本区域的引用。
十三、三色标记
黑色:自身和成员变量都已标记
灰色:自身已被标记,成员变量未被标记
白色:未被标记的对象
并发标记时可能会漏标,黑色多了成员变量,白色失去灰色的引用,可以将黑色标记为灰色
SATB把消失的引用推到GC的堆栈,扫描一遍标记,与rememberset配合不需要扫描整个堆
十四、GC调优
- JVM规划和预调优
选定最适合的垃圾回收器
计算内存需求
选定CPU
- 运行JVM运行环境
加大内存还是卡顿,内存越大,FGC时间越长,改用CMS或G1
- 解决JVM运行时出现的问题
CPU高怎么调优
有线程在占用系统资源
1.导出哪个进程CPU高(top)
2.该进程中的哪个线程CPU高(top -Hp #{pid})
3.导出进程中所有线程的堆栈(jstack #{pid}) 关注WAITING的,waiting on <0X00000000088ca3310>...等待哪个锁
找出哪个线程持有这把锁<0X00000000088ca3310>
4.查找哪个方法(jstack)
内存高怎么调优
1.导出堆内存(jmap)
2.分析(jhat、jvisualvm、mat、jprofile、jconsole)
生产一般不用图形化页面,会影响服务的效率,测试,压测的时候使用
使用jmap -histo #{pid}|head -20 (前20个)命令,查看哪个类比较多
生产配置 -XX:+HeapDumpOnOutOfMemoryError OOM时会自动产生堆转储文件,jmap -dump #{pid} 也会影响生产服务器,可以服务器隔离
jvisualvm可以导入dump文件
多个服务器高可用
在线定位:arthas