JVM总结:
Persion.java --> javac编译 --> Persion.class --> 类加载机制 --> JVM
类加载机制:
Persion.class --> 类加载机制 --> JVM
双亲委派
破坏:重写loadClass()等
Loading, Linking, and Initializing
(1)装载
a- 先找到类文件所在位置---- :磁盘 全路径 --> 类装载器 ClassLoad.find(String name) --> 寻找类
b- 类文件的信息交给JVM -->类文件字节码流静态存储结构 --> JVM中的某一区域 (方法区)
c- 类文件所对应的对象Class --> JVM --> 堆
(2)链接
a-验证
保证被加载类的正确性
b-准备
要为类的静态变量分配内存空间,并将其的值初始化默认值
static int a=10; -- 0
c-解析
将类中的符号引用转换为直接引用:符号引用(class文件中的代称),直接引用(实际内存中的地址)
String str [ace0 flag] = 地址是什么,对应内存中的某一个真实地址指向
(3)初始化
为静态变量,赋值为真正的值 a =10;
JVM运行时数据区(Run-Time Data Areas):
2.5.1. The pc Register
线程独享
The Java Virtual Machine can support many threads of execution at once (JLS 17). Each Java Virtual Machine thread has its own pc (program counter) register.
At any point, each Java Virtual Machine thread is executing the code of a single method, namely the current method ( 2.6) for that thread.
If that method is not native, the pc register contains the address of the Java Virtual Machine instruction currently being executed.
If the method currently being executed by the thread is native, the value of the Java Virtual Machine's pc register is undefined. The Java Virtual Machine's pc register i
Java虚拟机可以支持一次执行多个线程(JLS 17)。每个Java虚拟机线程都有自己的pc(程序计数器)寄存器。在任何时刻,每个Java虚拟机线程都在执行单个方法的代码,即该线程的当前方法(2.6)。
如果该方法不是本机的,则pc寄存器包含当前正在执行的Java虚拟机指令的地址。如果线程当前执行的方法是本机的,则Java虚拟机的pc寄存器的值是未定义的。Java虚拟机的pc寄存器i
2.5.2. Java Virtual Machine Stacks
线程独享,先进后出
一个线程的创建代表的是一个栈,每个方法被当前栈调用了,就代表一个栈帧
生命周期和线程一致
StackOverFlowError
栈帧:
局部变量表、操作数栈、动态链接(eg:类装载时还不能确定最终装载哪种类型List = List,要等到具体代码运行时才知道类型的这些)、方法返回地址
2.5.3. Heap
堆只有一个,线程共享的内存区域(非线程安全),生命周期和虚拟机一致
对象或者数组
OOM
2.5.4. Method Area
方法区只有一个,线程共享的内存区域(非线程安全),生命周期和虚拟机一致
类信息、常量、静态变量、即时编译器编译之后的代码
方法区逻辑上属于堆的一部分
OOM
JDK1.7 : PermSpace --> 永久代
JDK1.8 : MetaSpace --> 元空间
2.5.5. Run-Time Constant Pool
运行时常量池
Each run-time constant pool is allocated from the Java Virtual Machine's method area ( 2.5.4). The run-time constant pool for a class or interface is constructed when the class or interface is created ( 5.3) by the Java Virtual Machine.
每个运行时常量池都是从Java虚拟机的方法区域(2.5.4)中分配的。类或接口的运行时常量池是在Java虚拟机创建类或接口时构造的(2.5.3)。
2.5.6. Native Method Stacks
调用的C语言
JAVA对象内存布局
对象头:
Mark Word: 一系列的标记位(哈希码,分带年龄,锁状态标志等) 64位系统8字节
Class Pointer 指向对象对应的类元数据的内存地址 (即指向方法区中的类信息) 64位系统8字节
Length(数组对象特有的) 数组长度 4字节
实例数据:
包含了对象的所有成员变量,大小由各个变量类型决定 referencr:8字节
对齐填充:
为了保证对象的大小为8字节的整数倍
JVM内存模型:
MetaSpace Heap
设计内存分布:
年龄:一次垃圾回收,岁数+1,15
老年代:对象大小特别大的直接进入老年代,age>15
前提:大多数对象都是朝生夕死,生命周期比较短
Eden区相对连续,不会因为少量存活对象造成空间碎片
Survivor有可能形成空间碎片,-->2个
永远都能保证S0或者是S1的某一个为空,能够解决空间碎片问题(浪费了一个S区的空间而已)
Eden :S0 :S1 = 8 :1 :1
S区不够,跟老年代借用空间(担保机制)
young,old并非一定要young大old小,看实际程序执行调整
young GC包含Eden,S区:Minor GC
Old GC : MajorGC ,通常会伴随着MinorGC,也就意味着触发FullGC
FullGC:youngGC+OldGC
1.尽量减少GC频率
2.FullGC:STW尽可能减少FullGC频率(允许一定范围的YoungGC)
S0,S1一定要保证有一块是空的,每一次GC,对象的分带年龄+1
栈的深度不能设置过大:空间占用大,线程数少
过小:StackOverflow
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GC Root:
虚拟机栈中的本地变量、
static成员、常量引用(方法区)
本地方法栈中的变量Thread C 引用c语言
类加载器、
线程Thread
回收算法
1.标记和清除---GCRoot去标记堆内存中的不可达对象,清除,
缺点:
1.空间碎片,内存不连续
2.标记和清除都比较耗时(两次扫描全堆),效率比较低
2.复制算法:优势:空间连续
弊端:空间浪费(s0,s1)
3.标记-整理
--把垃圾回收算法进行落地 ---- 垃圾回收器
垃圾收集器还需要适合不同的代(分代:新生代、老年代)
新生代:复制算法--适应于少量对象存活的场景(新生代是朝生夕死)
老年代:标记-清除/标记-整理(大多数对象都存活,复制效率太低)
CMS Concurrent Mark Sweep 并发类的垃圾收集器
并发:用户线程和垃圾回收线程可以同时进行
并行:垃圾回收线程之间可以并行执行(多CPU多线程)
并发收集器 比较关注的是停顿时间---降低堆吞吐量的要求
CMS:允许清理时有垃圾的产生
初始标记 初始标记根据GCRoot去标记垃圾对象,停止用户线程 STW
并发标记 用户线程和标记线程同时执行
重新标记 多线程再次并行执行标记 STW (时间很短,多线程执行增量扫描新产生的垃圾而不是全量扫描)
并发清理 用户线程和gc线程同时执行
G1 :JDK 1.9默认的垃圾回收器(1.8推崇)
初始标记
并发标记
最终标记
筛选标记 根据用户设置的pause time 尽量满足这个时间,来筛选清理的垃圾,并不一定全部回收
Region区域 对堆重新进行了布局,逻辑上存在young old eden,物理上已经不是隔离的了
G1对比CMS:G1会对对象进行一个压缩处理,不会产生空间碎片,G1可以set pause time
是否选用G1的参考维度:
1.堆内存的使用率超过50%
2.对象分配率或提升率差异很大
3.应用程序正在经历不希望的长时间垃圾收集或压缩暂停(超过0.5到1秒)
JVM调优:
1.GC收集器:停顿时间和吞吐量
停顿时间 set pause time :垃圾回收器进行垃圾回收client执行响应的时间 --->很好的体验 --->和用户较多的交互场景,webn程序
吞吐量:运行用户代码时间/(运行用户代码时间+垃圾回收时间)用户代码执行占用cpu资源的时间比较大,跑任务,运算任务
串行收集 :Serial和Serial Old --->内存比较小、嵌入式的设备
吞吐量优先:Parallel Scavenge + Parallel Old -->并行类的收集器
停顿时间小:CMS G1(set pause time,not strict)适应于web应用 -->并发类的收集器
2.如何选择适合的垃圾收集器
1.如果对于停顿时间没有严格的要求,跑一遍自己的应用让虚拟机自己选择,调整堆大小来调整
2.内存空间小于100M或者 -XX:+UseSerialGC
3.如何应用是单线程运行而且没有停顿时间要求,让虚拟机自己选择,或者 -XX:+UseSerialGC
4.没有停顿时间要求且关注吞吐量的时候,让虚拟机自己选择,或者使用并行收集器 -XX:+UseParallelGC
5.对于停顿时间有要求或者严格的时候 -XX:+UseConcMarkSweepGC -XX:+UseG1GC
查询相关的命令
jps -l : 当前有哪些进程
jinfo -flag UseParallelGC 55516 是否使用了ParallelGC -XX:+UseParallelGC
(1)串行
-XX:+UseSerialGC
-XX:+UseSerialOldGC
(2)并行(吞吐量优先)
-XX:+UseParallelGC
-XX:+UseParallelOldGC
(3)并发收集器(响应时间优先)
-XX:+UseConcMarkSweepGC
-XX:+UseG1GC
JVM参数:
1.标准参数:不会随着JDK版本的变化而变化
java -version/help
2.-X参数
非标准参数:随JDK版本变动
Xint
3.-XX参数
a-Boolean类型
-XX:[+/-]name 启用/停止
b-非Boolean类型
-XX:name=value
-XX:MaxHeapSize=100M
4.其他参数[-XX参数]
-Xms100M ===> -XX:InitalHeapSize=100M
-Xmx100M ===> -XX:MaxHeapSize=100M
-Xss100k ===> -XX:ThreadStackSize=100k
展示JVM中的参数:jinfo -flags PID
如何修改参数:
1.idea。eclipse
2.java -XX:+UseG1GC xxx.jar
3.tomcat --bin-- xxx.sh/catalina.sh ---JVM参数
4.实时修改:jinfo修改 (只有manageable支持实时修改)
1Byte = 8bit
1KB=1024Byte
1MB=1024KB
1GB=1024MB
104857600 Byte --- 100M
调优利器:
1.JVM参数
命令
jps :查看当前java进程
jinfo : 查看或者修改JVM参数
NewRatio = 2 新老年代的比例默认是1:2 300M(100:200)
jstat -gc pid 1000 10
jstack pid :查看当前线程的堆栈信息 如果线程发生问题,方便排查(可以排查死锁等信息)
堆内存:
jmap:生成堆内存的快照
jmap -heap pid 查看堆内存信息
在发生OOM的时候,把堆内存打印出来 dump文件
jmap -dump:format=b,file=heap.hprof pid
工具
jdk自带的工具 bin/
jconsole
jvisualvm 可视化
java进程 本地或者远端
通过jvisualvm连接阿里云服务器tomcat进程[java进程]
阿里:arthas
MAT工具: Memory Analyzer 拿到堆内存,分析heap.hprof文件
Heap Analyzer
2.垃圾回收 GC日志 : -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:gc.log
一个是吞吐量,一个是停顿时间
不断选择垃圾回收器,探后观测吞吐量和停顿时间的最佳值
MetaSpace 区域的垃圾回收,回收缺失存在,数据量极少而已 20659k->20657k
垃圾收集的时机是什么时候?
Minor GC + Major GC + MetaSpace GC = Full GC
1.Eden区或者S区不够用 Minor GC :
2.老年代不够用 Major GC,伴随着Minor GC ===> 类似于FGC
3.方法区不够用了
4.System.gc() 手动调用(好像是FGC?)
评价一个垃圾收集器的好坏
停顿时间 + 吞吐量 + GC次数 工具:gcviewer java -jar gcviewer-1.36-SNAPSHOT.jar
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:gc.log (-XX:+UseConcMarkSweepGC -XX:+UseG1GC)
ParallelGC
G1GC:
Mixed GC Young区和部分old区的GC
达到45%即回收
[Eden: 142.0M(142.0M)->0.0B(142.0M) Survivors: 10.0M->10.0M Heap: 197.0M(254.0M)->55.0M(254.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
2020-06-30T15:43:33.951+0800: 15.095: [GC pause (Metadata GC Threshold) (young) (initial-mark), 0.0050694 secs]
JVM调优
内存空间不够用了
gc次数增多-->用户线程代码执行受影响 -->cpu使用会升高
尽可能的提高吞吐量,减少停顿时间
仅仅是使用UseG1GC时候 :G1能够设置停顿时间可控的情况 15ms 怎样取得吞吐量的最佳值
堆的大小不够用了,干脆就调整堆的大小,看参数是否有变化?
1.直接Heap增大 -Xms=500M -Xmx=500M
好的方向:吞吐量增加,GC次数减少,
不好的方向:停顿时间变大 原因:每一次垃圾回收时,周期都会被拉长
2.设置停顿时间 -XX:MaxGCPauseMillis=15 停顿时间不要太严格
分析:
优势:停顿时间可以被控制住
劣势:GC次数增加了 原因:GC回收时,没有完全回收掉,导致次数增多
3.启动并发GC时(堆内存使用占比)InitiatingHeapOccupancyPercent默认45%,也就是整个堆的45%使用率
堆的使用率达到45%(默认情况),即触发GC并发周期(触发region区域的GC) -XX:InitiatingHeapOccupancyPercent=45 -XX:InitiatingHeapOccupancyPercent=55
G1调优指南:
1.不要去调整young区大小: -Xmn -XX:+NewRatio ,这样会破坏停顿时间(是通过动态调整young区大小来实现的)
2.停顿时间的目标:可以设置停顿时间,找到吞吐量和停顿时间的最佳值,但是不要太严格,否则会增加GC次数
3.jvm参数调整:
-XX:InitiatingHeapOccupancyPercent: Use to change the marking threshold.
-XX:G1MixedGCLiveThresholdPercent and -XX:G1HeapWastePercent: Use to change the mixed garbage collection decisions.
-XX:G1MixedGCCountTarget and -XX:G1OldCSetRegionThresholdPercent: Use to adjust the CSet for old regions.
-XX:ConcGCThreads=n 增加垃圾回收标记线程数量
G1和CMS有何区别:
CMS:标记-清除 空间碎片
G1:标记-整理 减少空间碎片
G1: region :收集方式改变:1.优先收集垃圾比较多的区域
JVM性能优化
优化 --》 有问题 --》 现象展示出来
1.GC次数频繁怎么办?
反问:为什么你能够知道频繁?GC次数CPU使用率高了
专业:打出GC日志,观察是minor gc还是major gc,结合工具看一下
1、适当增加堆的内存空间
2、选择的垃圾收集器是否合适?大内存推荐G1(6G以上)
3、set pause time strict
4、堆内存使用率:InitiatingHeapOccupancyPercent默认45%是否启用了
2.CPU持续飙升怎么办?
反问:为什么你能够看到cpu飙升?
专业:top -- id --jstack、jinfo、jmap
1、机器方面是否需要做集群处理
2、mq去做延缓处理
3、业务层面,是否有死锁
3.OOM
dump文件,日志分析文件--MAT/其他工具
4。内存泄露和内存溢出OOM有区别吗?
内存泄露就是内存中的垃圾对象得不到及时的回收,慢慢变大,最终内存溢出
5.方法区中回收的是什么内容?
反问:方法区中存储的是什么:类信息,静态变量,常量
类的信息什么时候能够被回收?
1.堆里不在有该类对象了
2.加载该类的ClassLoader已经被回收了
3.java.lang.Class也不再有任何地方引用了 ,反射也不能使用
可以回收------不是一定回收(可以自救)
不可达对象一定要被回收吗?垃圾对象标记--最终标记确认
GC Root ---
不可达对象----finalize()----->可以将这个对象再次变为不是垃圾的对象
6.young gc会stop the world 吗
新生代的GC,都会停止用户线程,都会产生STW