JVM(JVM原理/性能监控/故障定位/OOM)
2021-10-13 18:48:09 1 举报
AI智能生成
为你推荐
查看更多
JVM
作者其他创作
大纲/内容
JRE是Java的运行环境,包括JVM标准实现及Java核心类库。
JVM是java虚拟机,是整个java实现跨平台的最核心的部分,能够运行以Java语言写作的软件程序
安装jdk时的会有两个jre文件夹
1.java内存结构图
JDK 1.8之前
JDK 1.8
2. 运行时内存区域图
当前线程所执行的字节码的行号指示器
线程私有
原因
1.2.1 程序计数器
是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame) 用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成 的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程
局部变量表
操作数栈:以压栈和出栈的方式存储操作数的
动态链接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态 连接(Dynamic Linking)。
图片
Java虚拟机栈和栈帧:Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法
1.2.2 虚拟机栈
Native方法本地方法栈与Java栈的作用和原理非常相似。
Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。
1.2.3 本地方法(Native Method)栈
每个类的信息(包括类的名称、方法信息、字段信息)
静态变量
常量
编译器编译后的代码
线程共享
存储已被JVM加载
1.2.4 方法区
Java中的堆是用来存储对象本身的以及数组(当然,数组引用是存放在Java栈中的)。
内存最大的一块,GC主要区域
从内存回收的角度来看,由于现在收集器基本都采用分代收集算法,所以Java堆中还可以细分年轻代,老年代,元数据区
JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池
运行时常量池
1.2.5 堆
在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,
它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。
避免在Java堆和Native堆中来回复制数据
1.2.6 直接内存(jdk1.4 NIO引入的)
3. 运行时内存区域
1.3 java 内存分配
1.4 对象的一辈子理解
2.内存区域
1.加载
2.验证
3.准备
4.解析
5.初始化
6.使用
7.卸载
具体过程(七步)
定义及过程
JAVA中执行顺序
程序的赋值步骤
什么情况下需要开始类加载过程的第一个阶段加载
启动类加载器(Bootstrap ClassLoader)
扩展类加载器(Extendsion ClassLoader)
应用程序类加载器(Application ClassLoader)
自己定义的类加载器
沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心 API库被随意篡改
避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一 次,保证被加载类的唯一性
为什么要设计双亲委派机制
有哪些类加载器
同一个JVM内,两个相同包名和类名的类对象可以共存,因为他们的类加载器可以不一样,所以看两个类对象是否是同一个,除了看类的包名和类名是否都相同之外,还需要他们的类加载器也是同一个才能认为他们是同一个。
模拟实现Tomcat的webappClassLoader加载自己war包应用内不同版本类实现相互共存与隔离
模拟实现Tomcat的JasperLoader热加载
Tomcat打破双亲委派机制
类加载器
双亲委派模型
3.java类加载机制
1. 那些区域需要回收
虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中,定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
1.类加载检查
在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择那种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
2.分配内存
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
3.初始化零值
初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
4.设置对象头
在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,<init> 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 <init> 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。
5.执行 init 方法
2. 对象的创建
存活对象较多的情况下比较高效
适用于年老代(即旧生代)
适用场合
标记和清除的效率都不高
效率问题
大量不连续的内存碎片,这样给大对象分配内存的时候可能会提前触发full gc。
空间问题
缺点
1. 标记-清除
存活对象较少的情况下比较高效
适用于年轻代(即新生代):基本上98%的对象是”朝生夕死”的,存活下来的会很少
需要一块儿空的内存空间
需要复制移动对象
2. 复制
用于年老代(即旧生代)
不会产生内存碎片
优点
需要移动对象,若对象非常多而且标记回收后的内存非常不完整,可能移动这个动作也会耗费一定时间
扫描了整个空间两次(第一次:标记存活对象;第二次:清除没有标记的对象)
3 标记-整理
Eden区
上一次GC的幸存者,作为这一次GC的被扫描者。
Survivor From
保留了一次MinorGC过程中的幸存者。
Survivor To
当Eden区满时
Minor GC触发条件
MinorGC采用复制算法
新生代
触发条件
MajorGC(Full gc)采用标记—清除算法
主要存放应用程序中生命周期长的内存对象
老年代
元空间(永久代)
流程图
栈上分配依赖于逃逸分析和标量替换
逃逸分析
1.对象栈上分配
栈上分配
cos同步
指针链撞
2.对象分配分配TLAB(Thread Local Allocation Buffer 线程本地分配缓存区)
-XX:SurivorRatio=8 新生代Eden区域与Survivor区域的容量比值,默认为8,代表Eden: Suvivor= 8: 1。
-Xms20M 设置堆内存的初始值
-Xmx 设置堆内存的最大值
-Xmn 设置新生代的大小
-XX:+PrintSCDetails
大量的对象被分配在eden区,eden区满了后会触发minor gc,可能会有99%以上的对象成为垃圾被回收掉,剩余存活的对象会被挪到为空的那块survivor区
3.对象优先分配在Eden
这个参数只在 Serial 和ParNew两个收集器下有效。
-XX:PretenureSizeThreshold=3145728 3M
4.大对象直接进入老年代
age
-XX:MaxTenuringThreshold=15
4.长期存活的对象进入老年代
直接晋级,不需要判定MaxTenuringThreshold
相同年龄所有对象的大小总和>Survivor空间的一半
5.动态对象年龄判定
Minor GC
大于
Full GC
不大于
允许担保失败
不允许担保失败
-XX:+HandlePromotionFailure(16Update 24之后不生效)
老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小
Minor GC 之前检查 老年代最大可用连续空间是否>新生代所有对象总空间
6.空间分配担保
新生代晋升老年代(对象内存分配和回收策略)
从内存回收的角度来看,所以Java堆中还可以细分
4. 分代收集算法
3. 垃圾收集算法
实现简单,判定效率高
循环引用
引用计数法
GC Roots→引用链
可达性分析算法
引用在,不回收
强引用
SoftReference
OOM前,把这些对象回收
如果仍OOM→抛出异常
软引用
WeakReference
下一次GC则清除
弱引用
PhantomReference
回收时→系统通知
虚引用
引用
4. 判断对象是否需要回收
5.Minor Gc和Full GC有什么不同呢
6.如何判断⼀个类是⽆⽤的类
4.垃圾回收机制
单线程,简单高效,历史悠久
它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。
Client模式
新⽣代采⽤复制算法,⽼年代采⽤标记-整理算法。
1. Serial收集器
2. Serial Old收集器
ParNew收集器就是Serial收集器的多线程版本
目前只有它能与CMS收集器配合工作
多线程,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作
多核CPU下对资源利用率会更高
新⽣代采⽤复制算法,⽼年代采⽤标记-整理算法
3. ParNew收集器
Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量
4. Parallel Scavenge收集器
使⽤多线程和“标记-整理”算法
5. Parallel Old收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
目标
标记—清除
回收算法
CMS仅作用于老年代
并发收集、低停顿
CMS收集器的内存回收过程是与用户线程一起并发执行的
特点
初始标记: 暂停所有的其他线程,并记录下直接与root相连的对象,速度很快 ;
并发标记: 同时开启GC和⽤户线程,⽤⼀个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为⽤户线程可能会不断的更新引⽤域,所以GC线程⽆法保证可达性分析的实时性。所以这个算法⾥会跟踪记录这些发⽣引⽤更新的地⽅。
重新标记: 重新标记阶段就是为了修正并发标记期间因为⽤户程序继续运⾏⽽导致标记产⽣变动的那⼀部分对象的标记记录,这个阶段的停顿时间⼀般会⽐初始标记阶段的时间稍⻓,远远⽐并发标记阶段时间短
并发清除: 开启⽤户线程,同时GC线程开始对为标记的区域做清扫。
工作流程
对CPU资源敏感;
⽆法处理浮动垃圾;
它使⽤的回收算法-“标记-清除”算法会导致收集结束时会有⼤量空间碎⽚产⽣。
6. CMS收集器
分区 Region
卡片 Card
堆 Heap
G1能充分利用多CPU、多核的硬件优势,来缩短Stop-The-World停顿时间
1.并行与并发
和其他收集器相同,分代概念依然保留。G1收集器不需要其他收集器的配合就可以管理整个堆,可以根据不同的方式去处理新创建的对象、存活了一段时间的对象和熬过多次GC的对象。
2.分代收集
不同于CMS的标记-清理,G1采用的是标记-整理的方式来处理碎片化的空间。这种方式意味着G1收集器运作期间不会产生内存空间碎片,收集后提供规整的可用空间
3.空间整合
G1相对于CMS另外一个更大的优势,就是可以设置可预测的停顿模型,能够使开发者明确指定在长度为M毫秒的时间片段内,消耗在垃圾收集器上的时间不能超过M毫秒。
4.可预测的停顿
分区概念
Minor GC
Mixed GC
G1提供了三种模式的垃圾回收
G1收集器的大概的工作过程
G1和CMS的比较
判断是否需要使用G1收集器
如何开启需要的垃圾收集器
7. G1收集器
jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk7
jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk8
jdk1.9 默认垃圾收集器G1
jdk9
8. jdk7、8、9默认垃圾回收器
9.作用的内存分区
停顿时间->垃圾收集器 进行 垃圾回收终端应用执行响应的时间吞吐量->运行用户代码时间/(运行用户代码时间+垃圾收集时间)
理解吞吐量和停顿时间
如何选择合适的垃圾收集器
5.垃圾收集器
方法调用
循环跳转
异常跳转
先暂停,没在安全点就继续跑
抢先式中断
看到标志就停
设置GC标志
主动式中断
安全点
安全域
GC ROOTS枚举效率
6. 什么时间节点回收
Xmx: 设置堆内存的最大值。
-Xms: 设置堆内存的初始值。
-Xmn: 设置新生代的大小。
-Xss: 设置栈的大小。
java -client -XX:+PrintFlagsFinal
-XX:+PrintCommandLineFlags
打印所有XX参数及值
1. GC相关的常用参数
子主题
2. 日志
-l:输出主类的全名,如果进程执行的是 JAR 包,则输出 JAR 路径
-v:查看虚拟机启动时显式指定的JVM参数列表
未被显示指定的可通过 jinfo -flag 查看
jps查看java进程
jstat -gc PID 1000 10
查看垃圾收集信息
查看某个java进程的类装载信息,每1000毫秒输出一次,共输出10 次
jstat -class PID 1000 10
-class:监视类加载、卸载数量、总空间以及类装载所耗费的时间
-gcutil:监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-gcnew:监视新生代的垃圾收集情况
-gcold:监视老年代的垃圾收集情况
每250毫秒查询一次进程2764垃圾收集状况,一共查询20次
jstat -gc 2764 250 20
查看2764进程各分区占比以及Minor GC和Full GC发生的次数和耗时
jstat -gcutil 2764
示例
jstat查看虚拟机性能统计信息
jinfo -flag MaxMetaspaceSize 18348
jinfo -flag <name> PID
查看某个java进程的name属性的值
jinfo -flag [+|-]<name> PID
布尔类型的JVM参数
jinfo -flag <name>=<value> PID
数字/字符串类型的JVM参数
调整JVM参数
jinfo实时查看和调整JVM配置参数
用于生成堆转储快照(一般称为heap dump或dump文件)还可以查询finalize执行队列、Java堆和方法区的详细信息,如空间使用率、当前用的是哪种收集器等
如何在发生堆内存溢出的时候,能自动dump出该文件
dump出堆内存相关信息
生成Java堆转储快照
jmap -dump
显示Java堆详细信息,如使用哪种回收器、参数配置、分代状况等
jmap -heap PID
显示堆中对象统计信息,包括类、实例数量、合计容量
jmap -histo:live PID
jmap生成堆转储快照
jhat命令与jmap搭配使用,来分析jmap生成的堆转储快照
jhat:堆转储快照分析工具
用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者 javacore文件)
通过top -Hp 48489可以查看该进程下各个线程的cpu使用情况
1.找到进程中执行最高的线程号
方法一:jstack 48489;方法二:jstack -l <pid> >jvm_listlocks.txt
2.用jstack pid (进程号)导出线程的 dump日志
3.由于jstack.log文件记录的线程ID是16进制,需要将top命令展示的线程号转换为16进制。把线程号转 16 进制 printf “%x \” 40437
4.到刚刚导出的 jvm_listlocks.txt 里面检索定位到定位到问题
解决死锁
jstack -l PID查看线程堆栈
getAllStackTraces()
扩展
jstack:Java堆栈跟踪工具
JDK命令行
一款基于JMX(Java Manage-ment Extensions)的可视化监视、管理工具
启动JConsole后,会自动搜索本机运行的所有JVM进程,相当于可视化的jps命令
“内存”页签的作用相当于可视化的jstat命令,用于监视被收集器管理的虚拟机内存的变化趋势
“线程”页签的功能相当于可视化的jstack命令,用于分析线程停顿的原因,比如死循环,锁等待
JConsole
功能最强大的运行监视和故障处理程序之一,曾经是Oracle官方主力发展的虚拟机故障处理工具
它除了常规的运行监视、故障处理外,还将提供其他方面的能力,譬如性能分析(Profiling)
优势:对应用程序实际性能的影响也较小,使得它可以直接应用在生产环境中
jps、jinfo、jstat、jstack、jmap、jhat等多种功能于一体
在Profiler页签中,VisualVM提供了程序运行期间方法级的处理器执行时间分析以及内存分析(生产不用)
BTrace动态日志跟踪插件
Java VisualVM
线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等
Java Mission Control:可持续在线的监控工具,可用于生产环境
JMC
一款基于服务性代理(Serviceability Agent,SA)实现的进程外调试工具
JHSDB
Java堆分析器,用于查找内存泄漏
MAT
可以比较不同的垃圾收集器的吞吐量和停顿时间
https://gceasy.io
阿里巴巴JVM调优工具
ArtThas
在线工具
GCViewer
GC日志分析工具
Aliabba jvmGenerate: 是一个提供 JVM 参数调优分析,JVM优化工具集,快速生成JVM优化配置,常见场景配置示例的工具网站。
XXFox (Java虚拟机参数分析)
https://thread.console.perfma.com
XSheepdog (Java线程Dump分析)
XElephant (Java内存Dump分析)
PerfMa
Fastt hread: 一个在线的 Thread dump分析网站,能帮助我们判断 CPU 峰值、死锁、内存异常、应用反应迟钝、响应时间变长和其他系统问题。
https://fastthread.io
Fastt hread
堆Dump可视化分析
https://heaphero.io/
heaphero
Thread dump分析网站
可视化界面在线生成JVM参数
jvmmemory
JVM调优的在线网站
可视化故障处理工具
3.JVM监控工具
先用 top 显示进程列表,找到占用率最高的 PID
再使用 top -Hp pid 找到占用最高的线程 tid
printf '%x\' tid 将线程ID转换为16进制 (小写)
最后使用 jstack pid | grep tid -A60
https://jvmmemory.com/
CPU 占用过高
每隔2秒打印一次CPU信息,共打印3次
cs:表示上下文切换次数(context switch)
vmstat 2 3
针对 pid 进行上下文切换监控
cswch/s:每秒自愿上下文切换(进程无法获取到可供执行的资源从而自愿发生上下文切换)
nvcswch/s:每秒非自愿上下文切换(进程时间片用完、被高优先级进程抢走、系统中断等发生的非自愿的场景)
pidstat -w pid
频繁上下文切换
CPU
代码举例
原因:线程请求栈的深度超过当前Java虚拟机栈的最大深度(默认大小是1M,1024KB)
解决:检测是否有无穷递归、死循环,如果不是bug,可以通过调整虚拟机栈的大小来解决(-Xss2m)
栈内存溢出StackOverflowError
原因:堆内存被用光,或者内存泄漏堆积导致
修复内存泄露,或使用 -Xmx 增加堆内存大小
对象太多(Java heap space)
使用 -XX:-UseGCOverheadLimit 取消 GC 开销限制
GC回收超时(GC overhead limit exceeded)
打印的堆栈跟踪信息,使用操作系统本地工具进行诊断
本地内存不足(Direct buffer memory)
减少虚拟机栈的大小或增大虚拟机占用比例
线程太多(unable to create new native thread)
使用-XX: MaxMetaSpaceSize 增加 metaspace 大小
元空间内存不足(Metaspace)
OOM
堆内存溢出OutOfMemory
内存溢出
程序在申请内存后,无法释放已申请的内存空间
每秒打印一次GC详细信息(C:容量|U:已使用)(E:Eden|O:Old|M:MetaSpace)
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
jstat -gc pid 1000
查看最近3次GC空间占比
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
jstat -gcutil 1000 3
使用 jmap 生成 堆转储 快照文件 jmap -histo:live pid
通过 (Thread dump分析工具)找出是哪块代码
导致频繁 GC
内存泄漏
左上角显示Threads: 28 total,下面显示详细的线程信息
pstree -p 8379 | wc -l
ls /proc/8379/task | wc -l
ps hH p 8379 | wc -l
线程数
top -H -p pid
查看线程信息
内存
4.故障定位
上线前:根据需求(支撑多少QPS)进行JVM规划和预估调优(几台机器,多大内存,堆内存划分)
上线初期:根据日志优化JVM运行环境(解决慢、卡顿问题)
上线后期:解决JVM运行过程中出现的各种问题(OOM,逃逸分析,频繁 full gc)
调优阶段
-XX:MaxGCPauseMillis:停顿时间(建议)
GC会尝试各种手段达到这个时间,比如减小年轻代
垃圾收集器做垃圾回收中断应用执行的时间
停顿时间
-XX:GCTimeRatio = n(默认值99)即 1% 时间用于垃圾收集
例如将 n=19 则垃圾收集时间为1/(1+19) 即 5% 时间用于垃圾收集
GC时间占用程序运行时间的百分比:1/(1+n)
GC时间比例
吞吐量=应用程序时间/(应用程序时间+垃圾收集时间) 即 1-1/(1+n)
吞吐量
调优指标
通过 java 命令查看
java -version
java -help
常用参数
标准参数,-开头
通过 java -X more 查看
-Xms200m -Xmx200m:最小堆和最大堆
-Xmn60m:新生代
-Xss1m:栈空间大小
防止内存震荡(否则JVM会将堆内存进行扩容和缩容)
扩展和回缩需要大量的计算,影响程序的执行效率
扩展:为什么最小堆和最大堆设置一样大?
-X开头
通过 java -XX:+PrintFlagsFinal -version | more 命令查看
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintHeapAtGC
-XX开头
非标准参数
调优参数
-XX:SurvivorRatio:调整eden和surviovor区域的大小
-XX:PreTenureSizeThreshold:大对象到底多大
-XX:MaxTenuringThreshold:设置的是年龄阈值,默认15(对象被复制的次数)
-XX:+ParallelGCThreads:并行收集器的线程数,一般设置为和CPU核数相同(同样适用于CMS)
-XX:+UseAdaptiveSizePolicy:自动选择各区大小比例
Parallel常用参数
-XX:+UseConcMarkSweepGC:启用CMS收集器
-XX:ParallelCMSThreads:CMS线程数量
使用多少比例的老年代后开始CMS收集,默认是68%(近似值)如果频繁发生SerialOld卡顿,应该调小(频繁CMS回收)
-XX:CMSInitiatingOccupancyFraction
-XX:+UseCMSCompactAtFullCollection:在FGC时进行压缩
默认情况下GC是不回收PermGen的空间的(其名字也能反映这点)这个选项就是启用清除PermGen的功能,删除不用的classes
-XX:+CMSClassUnloadingEnabled
达到什么比例时进行Perm回收
-XX:CMSInitiatingPermOccupancyFraction
设置GC时间占用程序运行时间的百分比
-XX:GCTimeRatio
停顿时间(建议)GC会尝试各种手段达到这个时间,比如减小年轻代
-XX:MaxGCPauseMillis
CMS常用参数
-XX:+UseG1GC
建议值,G1会尝试调整Young区的块数来达到这个值
GC的间隔时间
-XX:+GCPauseIntervalMillis
分区大小,建议逐渐增大该值
-XX:+G1HeapRegionSize
新生代最小比例,默认为5%
-XX:G1NewSizePercent
新生代最大比例,默认为60%
-XX:G1MaxNewSizePercent
GC时间建议比例,G1回根据这个值调整堆空间
线程数量
-XX:ConcGCThreads
启动G1堆空间占用比例
-XX:InitiatingHeapOccupancyPercent
G1常用参数
GC调优
JVM堆栈大小设置?
JVM 调优,让其几乎不发生 Full GC?
日均百万交易系统
(1)50%以上的堆被存活对象占用 (2)对象分配和晋升的速度变化非常大 (3)垃圾回收时间比较长
是否选用G1垃圾收集器的判断依据
(1)使用G1GC垃圾收集器: -XX:+UseG1GC
(2)调整内存大小再获取gc日志分析
(3)调整最大停顿时间
(4)启动并发GC时堆内存占用百分比
调优
(1)不要手动设置新生代和老年代的大小,只要设置整个堆的大小
(2)不断调优暂停时间目标
(3)使用-XX:ConcGCThreads=n来增加标记线程的数量
(4)MixedGC调优
(5)适当增加堆内存大小
最佳指南
G1调优与最佳指南
5.调优流程
7.性能优化
JVM(JVM原理/性能监控/故障定位/OOM)
0 条评论
回复 删除
下一页