JVM
笔记
类加载机制
jvm
整体结构和内存模型
jvm对象创建与内存分配
G1/ZGC/调优⼯具
垃圾收集
内存回收
类加载过程
双亲委派
算法(判断对象死亡)
常⻅引⽤类型
finalize()⽅法判断对象最终是否存活
对象的创建
对象⼤⼩与指针压缩
对象栈上分配
对象分配
第一次标记并进⾏筛选(没有覆盖finalize()⽅法直接回收)
第⼆次标记
Eden、
⼤对象直接进⽼年代
⻓期存活的对象进⼊⽼年代
对象动态年龄判断(survivo r区)
⽼年代空间分配担保
引导类加载器:负责加载位于
jre/lib
下面的核心类库
扩展类加载器:负责加载位于
jre/lib
目录下
ext
扩展目录中的
jar
包
应用程序类加载器:负责加载
classpath
路径下的类包,就是自己写的类
其他类加载器:加载用户自定义路径下的类包
先找父类加载,父类找不到再自己加载
概括
1
沙箱安全机制:防止核心
API
库被篡改
避免类的重复加载:保证被加载类的唯一性
全盘负责委托机制:当一个
CLASSLOADER
引入一个类时,除非显示的使用另外一个
classloader
该类所依赖以及引用的类也由这个
classloader
载入
自定义类加载器:继承
java.lang.classloader
类,两个核心方法
打破双亲委派:需要重新
loadClass
方法,实现自己的加载逻辑,不委派给双亲
tips:JVM
内,相同包名和类名的对象可以共存,因为他们的类加载器可以不一样,所以看两个类对象是否是同一个,
还要看他们的类加载器是否一样
loadClass(String,boolean)
实现双亲委派
findClass
GcRoots
可达性算法分析
引⽤计数法
线程栈的本地变量
静态变量
本地⽅法栈的变量
逃逸分析
标量替换:标量、聚合量
对象⼤⼩可以根据JOL-CORE包查看
jdk1.6开始64位操作系统⽀持指针压缩-XX:+UseCompressedOops()默认开启
找出结论间的共性
类加载检查
分配内存
初始化
设置对象头
执⾏init⽅法
强引⽤
软引⽤
弱引⽤
虚引⽤
按照程序员的意思进⾏初始化
MarkWord标记字段
(32位占四字节,64位8字节)
KlassPointer类型指针(开启压缩4字节,关闭压缩8字节),类的元数据的指针
数组⻓度(4字节只有数组对象才有)
分配到的内存空间初始化为零值(不包括对象头)
哈希值
gc分代年龄
锁状态标志
线程持有锁
偏向线程ID
偏向时间戳
划分内存的⽅法
解决并发问题
指针碰撞(BumpThePointer)默认⽅法
空闲列表(FreeList)
虚拟机遇到new指令先去检查这个指令的参数能否在常量池中定位到一个类的符号引⽤,
并且检查这个符号引⽤代表的类是否已经被加载、解析和初始化过。如果没有,先加载类
CAS(compareandswap)
本地线程分配缓冲(ThreadLocalAllocationBufferTLAB)
每个线程在堆中预先分配一⼩块内存
java.exe
调用底层的
jvm.dll
创建
JAVA
虚拟机(
C++
)
创建一个引导类加载器(
C++
)
C++
调用
java
代码创建
jvm
启动器,
sun.misc.launcher
(单例)
该类由引导类加载器负责加载创建其他类加载器
sun.misc.launcher.getLauncher.getclassloader.loadclass("com.**.Test.java")
加载完成后汇之星
test
。
main
()方法入口,运行结束后
JVM
销毁
概括
1
加载:在硬盘上查找并读入字节码文件,使用到类时才会加载
加载阶段会在内存中生成代表这个类的
java.lang.class
对象,
作为方法区这个类的各种数据的访问入口
验证:校验字节码文件的正确性
准备:给累的静态变量分配内存,并赋予默认值
解析:将符号引用替换为直接引用,将静态方法替换为指向数
据所存内存的指针或者句柄,这就是静态链接过程。
动态链接是在程序运行期间完成的符号引用替换为直接引用。
初始化:对类的静态变量初始化为指定的值,执行静态代码块
使用
卸载:
tips:
类被加载到方法区中后主要包含:运行时常量池、类型信息、字段信息、方法信息、
类加载器的引用、对应
class
实例的引用等
垃圾收集算法
垃圾收集器
G1收集器(-XX:+UseG1GC)
ZGC收集器(-XX:+UseZGC)
调优⼯具
年轻代
⽼年代
jvm参数
垃圾收集底层算法
三⾊标记
多标-浮动垃圾
漏标-读写屏障
跨代引⽤
‐Xms3072M‐Xmx3072M‐Xmn2048M‐Xss1M‐XX:MetaspaceSize=256M
‐XX:MaxMetaspaceSize=256M‐XX:SurvivorRatio=8
很多优化⽆⾮就是让短期存活的对象尽量都留在survivo r⾥,
不要进⼊⽼年代,这样在minorgc的时候这些对象都会被回收,
不会进到⽼年代从⽽导致fullgc。
记忆集和卡表
在新⽣代可以引⼊记录集(RememberSet)
hotspot使⽤一种叫做“卡表”(cardtable)的⽅式实现记忆集,
增量更新incrementalupdate
原始快照
snapshotatthebeginning
SATB
写屏障
并发标记时对漏标的处理⽅案
Jmap
Jstack
Jinfo
Jstat
写屏障实现SATB
写屏障实现增量更新
jstat-gcpid最常⽤,可以评估程序内存使⽤及GC 压⼒整体情况
jstat-gccapacitypid
jstat-gcnewpid
jstat-gcnewcapacitypid
jstat-gcoldpid
jstat-gcutilpid
年轻代对象增⻓的速率
可以执⾏命令jstat-gcpid100010(每隔1秒执⾏1次命令,共执⾏10次)
那么可以执⾏命令jstat-gcpid30000010,观察每次结果eden,survivor和⽼年代使⽤的变化情况,
Jinfo-flagspid
Jinfo-sysprops pid
serial收集器-XX:+UseSerialGC
-XX:+UseSerialOldGC
parn ew
parallel
-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(⽼年代)
serail收集器的多线程版本,默认收集线程和CPU核数相同
也可以⽤参数(-XX:ParallelGCThre ads)指定
关注点是吞吐量(⾼效率的利⽤CPU)
新⽣代复制算法,⽼年代标记整理算法STW
JDK8默认使⽤
-XX:+UseParNewGC可以和CMS收集器配合使⽤。
新⽣代复制算法,⽼年代标记整理算法STW
单线程-stoptheword
简单⾼效,没有线程交互开销
CMS:写屏障+增量更新
G1,Shenandoah:写屏障+SATB
ZGC:读屏障
找出占⽤cpu最⾼的线程堆栈信息
Jmap-histoid>log.txt
Jmap-heappid
jma p‐dump:format=b,file=eur eka.hprof14660
使⽤命令top-p<pid>,显⽰你的java进程的内存情况,pid是你的java进程号,⽐如19663
按H,获取每个线程的内存情况
找到内存和cpu占⽤最⾼的线程tid,⽐如19664
转为⼗六进制得到0x4cd0,此为线程id的⼗六进制表⽰
执⾏jstack19663|grep-A104cd0,得到线程堆栈信息中4cd0这个线程所在⾏的后⾯10⾏,
从堆栈中可以发现导致cpu飙⾼的调⽤⽅法
查看对应的堆栈信息找出可能存在问题的代码
就是当灰⾊对象要删除指向⽩⾊对象的引⽤关系时,就将这个要删除的引⽤记录下来,在并发扫描结束之后,
再将这些记录过的引⽤关系中的灰⾊对象为根,重新扫描一次,这样就能扫描到⽩⾊的对象,将⽩⾊对象直
接标记为⿊⾊(对象也有可能是浮动垃圾)
增量更新就是当⿊⾊对象插⼊新的指向⽩⾊对象的引⽤关系时,就将这个新插⼊的引⽤记录下来,
等并发扫描结束之后,再将这些记录过的引⽤关系中的⿊⾊对象为根,重新扫描一次。
针对并发标记(还有并发清理)开始后产⽣的新对象,
通常的做法是直接全部当成⿊⾊,本轮不会进⾏清除。
在并发标记过程中,如果由于⽅法运⾏结束导致部分局部变量(gcroot)被销毁
这个gcroot引⽤的对象之前⼜被扫描过(被标记为⾮垃圾对象),
那么本轮GC不会回收这部分内存。
⿊⾊
灰⾊
⽩⾊未被访问过,开始到结束都是⽩⾊表⽰不可达要垃圾回收
已经被垃圾收集器访问过,但⾄少还有一个应⽤未扫描
对象已经被垃圾收集器访问过,所有引⽤也被扫描
⿊⾊代表扫描过,安全存活,⿊⾊不可能直接指向⽩⾊对象
CMS
SERALOLD
paralleloldParallelScavenge收集器的⽼年代版本。
serial的⽼年代版本,CMS收集器后背⽅案,
JDK1.5之前和parallel搭配
ZGC是一款JDK11中新加⼊的具有实验性质的低延迟垃圾收集器
⽀持TB量级的堆。最⼤GC停顿时间不超10ms。奠定未来GC特性的基础。最糟糕的情况下吞吐量会降低15%。
不分代(暂时)
⼩型Region中型Region⼤型Region
G1(Garbage-First)是一款⾯向服务器的垃圾收集器,主要针对配备多颗处理器及⼤容量内存的机器,复制算法
G1将Java堆划分为多个⼤⼩相等的独⽴区域(Region)JVM最多可以有2048个Region。
Region⼤⼩等于堆⼤⼩除以2048,-XX:G1HeapRegionSize"⼿动指定Region⼤⼩,
年轻代和⽼年代,都是region集合,可以不连续。年轻代默认5%“-XX:G1NewSizePercent”
设置新⽣代初始占⽐,最多不超过60%可以通过“-XX:G1MaxNewSizePercent”调整。
G1有专⻔分配⼤对象(超过了一个Region⼤⼩的50%,)的Region叫Humongous区,
初始标记STW-》并发标记-》最终标记STW-》筛选回收stw
YoungGC/MixedGC /FullGC
什么场景适合使⽤G1
50%以上的堆被存活对象占⽤
对象分配和晋升的速度变化⾮常⼤
垃圾回收时间特别⻓,超过1秒
8GB以上的堆内存(建议值)
停顿时间是500ms以内
MixedGC
不是FullGC,⽼年代的堆占有率达到参数(-XX:InitiatingHeapOccupancyPercent)设定的值则触发,
回收所有的Young和部分Old(根据期望的GC停顿时间确定old区垃圾收集的优先顺序)以及⼤对象区,
正常情况G1的垃圾收集是先做MixedGC,主要使⽤复制算法,需要把各个region中存活的对象拷⻉到
别的region⾥去,拷⻉过程中如果发现没有⾜够的空region能够承载拷⻉对象就会触发一次FullGC
一种以获取最短回收停顿时间为⽬标的收集器。(提⾼⽤⼾体验)
标记清除算法/-XX:+UseConcMarkSweepGC(o ld)
初始标记-》并发标记-》重新标记-》并发清理-》并发重置
CPU资源敏感/⽆法处理浮动垃圾(并发标记和并发清理产⽣的垃圾⽆法处理)
/标记-清理算法导致⼤量的空间碎⽚-
XX:+UseCMSCompactAtFullCollection可以让jvm在执⾏完标记清除后再做整理
/执⾏过程不确定性,收集器未执⾏完再次启动consurrentmodelfailure
初始标记:STW,记下GCROOTS能直接引⽤的对象,速度很快
并发标记:从GCROOTS遍历。⽤⼾线程可能导致已标记对象变化状态
重新标记:修正并发标记⽤⼾线程导致标记变动的那部分对象的标记记录
⽤到三⾊标记⾥⾯的增量更新算法做重新标记STW
并发清理:开启⽤⼾线程,同时GC会对未标记的区域做清扫。此阶段新增
对象会标记为⿊⾊不做处理
并发重置:重置本次GC过程的标记数据
筛选回收阶段⾸先对各个Region的回收价值和成本进⾏排序,
根据⽤⼾所期望的GC停顿时间(可以⽤JVM参数
-XX:MaxGCPauseMillis指定)来制定回收计划,
分代收集理论
复制算法
标记整理算法
标记清除算法
1效率问题,标记对象多效率不⾼
2空间问题,产⽣⼤量不连续碎⽚
新⽣代:⼤量死亡-标记-复制
⽼年代-标记整理/清除
(⽐复制算法慢⼗倍以上)
jvm虚拟机
概括各组思想
总结句避免使⽤缺乏思想的句⼦
总结句要说明⾏动产⽣的结果
找出结论间的共性
类加载⼦系统
运⾏时数据区
字节码执⾏引擎
堆-Xms-Xmx
栈-Xss
本地⽅法栈
⽅法区(元空间)-XX:MetaspaceSize
-XX:MaxMetaspaceSize
程序计数器
常量,静态变量,类信息
局部变量表
操作数栈
动态链接
⽅法出⼝
年轻代1/3-Xmn
⽼年代2/3
eden(8/10)
survivo r
S0(1/10)
S1(110)
评论0
最新资源