JVM
2021-06-19 00:04:26 0 举报
AI智能生成
jvm随记
作者其他创作
大纲/内容
JVM
即时编译(JIT)(有利有弊、优化1000次以上的循环)
JIT
编译简介
编译器
编译步骤
逃逸分析
概念 : 确定指针动态范围的方法
新对象仅对该线程可见,外部线程不可见,否则则发生逃逸
发生时机
发生在JIT编译阶段,收集到足够的jvm运行数据,更好的判断对象是否逃逸
java逃逸分析时方法级别的,因为JIT也是方法级别的
判断依据
对象被赋值堆中对象的字段(全局变量)、类的静态变量
对象被传进不确定的代码中运行
满足任一项则判定为逃逸
优化手段
栈上分配:
如果某个对象在子程序被分配,且指向该对象的指针永远不会逃逸,该对象可以分配在栈上,而不是堆上。可降低垃圾回收频率。
同步锁消除:
该对象只能从一个线程访问,且该对象上的操作不需要同步
分离对象/标量替换:
如果该对象的访问方式不要求对象分配在连续的内存结构,那么对象的部分/全部可以不分配在内存是内存上,而是CPU寄存器上
基于逃逸分析的对象分配
是否逃逸?没有则分配在栈上
降低垃圾回收频率,提高性能
否则分配在堆上,是否为大对象?是则进入老年代
-XX:PretenureSizeThreshold : 大于这个值的参数直接在老年代分配
-XX:PretenureSizeThreshold
新生代是否开启线程本地分配缓存(TLAB)-占Eden 1% - (默认开启)
确保多线程对象分配安全
TLAB放不下/没开,则通过CAS乐观锁分配到Eden
JVM调优
性能监控
常用命令
jps:查看java进程状态
jps -l :输出主类全名、是jar包则输出jar路径
jps -v :查看jvm启动时显式指定的Jvm参数(jinfo -flag 查看非显式参数)
jps -m : 输出传递给main方法的参数,在嵌入式jvm上可能是null
jinfo:实时查看和设置JVM参数
jinfo
查看jvm参数
jinfo PID : 显示Jvm详细参数,末尾显示默认启动参数
jinfo -flag name pid :查看指定的 jvm 参数的值
jinfo -flags pid : 输出全部的参数
jinfo -sysprops pid:输出当前 jvm 进行的全部的系统属性
动态修改jvm参数(并不是所有的参数都支持动态修改( 不重启虚拟机的情况下))
jinfo -flag [+|-]name pid :(布尔类型)
jinfo -flag name=value pid:(值、字符串)
jstat:查看虚拟机状态
jstat
jstat –class<pid> : 监视类加载器、卸载数量、总空间及加载耗时等
jstat -compiler <pid> : 显示VM实时编译的数量等信息
GC相关
jstat -gc <pid>: 可以显示gc的信息,查看gc的次数,及时间
jstat -gc 6580 250 20 : 每250ms查询一下6580进程,查询20次
jstat -gcutil <pid>:统计gc信息
jstat -gcutil 6580 :查询6580进程各分区占比,以及MinorGC和FullGC的次数和耗时
jstat -gcnew <pid>:年轻代对象的信息。
jstat -gcnewcapacity<pid>: 年轻代对象的信息及其占用量
jstat -gcold <pid>:old代对象的信息。
jstat -gcoldcapacity <pid>: old代对象的信息及其占用量。
jstat -gcpermcapacity<pid>: perm对象的信息及其占用量。
jmap:查看jvm内存快照
jmp
堆中对象的统计和内存快照
生成快照信息的其他方式
应用
开发\\测试环境,分析环境内存在用
生产环境
Arthas(阿尔萨斯)是阿里巴巴开源的 Java 诊断工具
Arthas
jmap pid :查看进程的内存映像信息
jmap -heap pid : 打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和各内存区域内存使用信息
jmap -histo pid
查看线程有多少类,类有多少对象,占多少内存
jmap -histo pid | head 20 : (linux)列举前20个类的对象和内存占用
jmap -histo:live pid : 显示堆中对象的统计信息
jmap -clstats pid :打印类加载器信息
jmap -finalizerinfo pid : 打印等待终结的对象信息
常见分析工具
MAT(常用)
JProfiler:收费
jhat:堆转储分析工具(jdk自带)
jvisualvm:文件->装入hprof
jstack:查看java堆栈信息
jstack
查看线程dump文件、javacore文件(线程堆栈快照)
定位线程死锁、死循环、线程挂起等线程异常
jstack检测cpu高
1. top
2. top -H -p pid 找出tid
3. printf \"%x\\" tid : 线程转换成16进制
4. jstack pid | grep tid(16进制) -A 30
jstack -l pid : 查看线程堆栈信息
jstack -f pid: 当’jstack [-l] pid’没有相应的时候强制打印栈信息
jstack -m pid : 打印java和native c/c++框架的所有栈信息
拓展(getAllStackTraces)
常用可视化分析工具
JConsole(jdk自带)
通过JMX的MBean(Managed Bean)对系统进 行信息收集和参数动态调整
启动后会自动搜索所有JVM进程,等同于动态的jps
“内存”页签相当于jstat,监视虚拟机内存变化
“线程”页签,相当于jstack检测线程状态
Java VisualVM(jdk自带)
Oracle 自带主推
运行监视、故障处理、性能分析(Profiling)
Profiler页签,监测程序运行时间和内存变化(非生产)
Btrace 动态日志跟踪
下载解压,启动:java -jar arthas-boot.jar,输入编号回车,即可查看启动日志
dashboard:展示当前进程信息,ctrl+c 中断执行
thread
显示所有线程信息、对应CPU占用情况
thread -pid : 查看线程在干嘛
thread -d : 监测死锁
jad 类的全路径名
显示该类的加载器和location
反编译
查看代码版本
查看动态代理对象
redifine /root/***/**.class
使线上修改的代码立即生效(慎用)
原理 : classloader重新加载
trace 全限定类名方法名
分析方法运行的时间、运行细节
方法调用追踪
JMC:Java Mission Control(jdk自带)
持续监控,可用于生产环境
JHSDB : 基于服务性代理实现的进程外调试工具
GC日志分析工具
GCEasy
GCViewer
故障定位
CPU
CPU占用高
1. top查看线程列表,占用率最高的pid
2. 使用top -hp pid 找到CPU占用最高的线程tid
业务代码 : 检查代码质量:死循环、逻辑计算量大
垃圾回收线程,检查是否频繁FullGC
正常FullGC,可能是服务器压力大
FullGC内存回收很少:内存泄漏
3. printf \"%x\\" tid : 线程ID转换16进制(小写)
4. jstack pid | grep tid(16进制) -A 60
频繁上下文切换
Linux vmstat
Linux vmstat
vmstat 2 3 :每两秒打印一次CPU信息,打印三次
CS : 表示上下文切换(Constant Switch)
Linux pidstat
Linux pidstat
pidstat -w pid
针对pid进行上下文切换监控
cswch/s:线程自愿上下文切换
nvcswch/s:线程非自愿上下文切换(时间片用完、优先级高的线程抢走、系统中断等特殊场景)
内存
内存溢出
栈溢出(StackOverflowError)
线程请求的栈深度大于虚拟机允许的最大深度(默认1M)
检查是否有死循环、无穷的递归,非BUG可以调整栈大小(设置-Xss5m设置最大调用深度)
堆溢出(OutOfMemoryError)
原因
堆内存被用光,内存泄漏堆积
虚拟机在扩展栈深度时,无法申请到足够的内存空间
OOM
对象太多
修复内存泄漏,回收无用对象、调整堆大小(-Xmx)
GC回收超时
-XX:MaxGCPauseMillis=300 : 修改GC的最大停止时间(个人)
-XX:-UseGCOverheadLimit :取消GC开销限制
示例
本地内存不足
打印堆栈信息,结合使用系统工具进行分析
线程太多
减小栈大小、减小堆占用比例
元空间内存不足
-XX:MaxMetaspaceSize : 增加元空间内存大小
内存泄漏
原因 : 程序在申请内存后,无法释放已申请的内存空间
导致频繁GC
jstat -gc pid 1000
每秒打印一次GC信息(C:容量 | U: 已使用)(E: edon | O: old | M: MetaSpace)
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT CGC CGCT GCT
jstat -gcutil pid 1000 3
每秒一次,查看最近三次GC空间占比
S0 S1 E O M CCS YGC YGCT FGC FGCT CGC CGCT GCT
使用jmap生成内存快照信息
通过MAT的Histogram判断代码片段
线程信息
top -H -p pid
左上角显示总线程数,下面具体线程
线程数
pstree -p pid | wc -l
ls /proc/pid/task | wc -l
ps hH p pid | wc -l
调优阶段
上线前
根据业务QPS评估,jvm规划和jvm预调优(几台机器、机器内存、堆栈划分)
上线初期
根据jvm日志解决卡顿、响应慢
线上后期
解决OOM、逃逸分析、频繁fullGC
调优指标
停顿时间(STW):垃圾回收中断用户线程(应用执行)的时间
减小STW
降低对象创建速率
增大年轻代空间
-Xmn :指定年轻代的大小
-XX:NewRatio :指定年轻代相对于老年代的大小比例
选择合适的GC算法
G1 中,可以使用系统属性 -xx:MaxGCPauseMillis来设置 GC 预期最大停顿时间
进程是否使用了 Swap 分区
分配更多的物理内存
减少在服务器上运行的进程的数量,以便它可以释放内存(RAM)
减少应用程序的堆大小(我不建议这么做,因为它会导致其他副作用。不过,它可能会解决你的问题)
调整 GC 线程数
后台 I/O 活动
显示调用了 System.gc()
GC时间比例:GC时间占运行时间的比例(1/(n+1))
-XX:GCTimeRatio=19 ,则垃圾收集时间为1/(1+19),即5%时间用于垃圾收集
吞吐量:应用时间/(应用时间+GC时间)
1-(1/(n+1))
调优参数
标准参数(-开头)
java命令查看
java -version
java -help
非标准参数(-X开头)
java -X more 查看
设置最大堆和最小堆大小一致?
防止内存震荡:jvm对内存扩容、收缩
扩容和回缩需要大量计算,影响执行效率
-Xmn<size> 为年轻代(新生代)设置初始和最大堆大小
-Xms<size> 设置初始 Java 堆大小
-Xmx<size> 设置最大 Java 堆大小
-Xss<size> 设置 Java 线程堆栈大小
非稳定参数(-XX开头)
java -XX:+PrintFlagsFinal -version 查看
查看-XX参数
-XX:+PrintGC(查看GC基本信息)
-XX:+PrintGCDetails(查看GC详细信息)
-XX:+PrintHeapAtGC(查看GC各域变化情况)
调优案例(亿级流量)
如何设置堆大小?
几乎不发生FullGC?
线程死锁
死锁条件
互斥
请求和保持等待
不可剥夺
环路等待
预防死锁
破坏死锁条件
指定加锁顺序、设置超时时间
银行家算法
保证分配资源后不进入不安全状态
死锁检测
Jconsole
jvm
组成结构
加载子系统(按需加载)
类对象
对象内存结构
对象头(Header)
Mark Word
哈希吗
GC分代年龄
锁状态标志
线程持有锁
偏向锁ID
偏向锁时间戳 等
占用8字节
类型指针
对象所属类的元信息在方法区的内存地址
开启指针压缩
4字节
不开启指针压缩
8字节
数组长度
若对象为数组,jvm无法计算该对象大小,需要在对象头记录数组长度
占4字节
关闭指针压缩会涉及头部填充
实例数据(instance data)
对象的有效信息(对象中所定义的各类型字段内容)
对齐填充(padding)
对象大小必须为8字节的整数倍,填充保证知字节对齐(非必须)
对象创建过程
1. jvm遇见new时,先在常量池检测,类有没有被加载,否则先加载
2. 检查通过后为对象分配内存
指针碰转(堆内存规整)
使用的一边,空闲的一边;分配时指针往空闲内存移动一个对象大小
空闲列表(堆内存不规整)
jvm列表记录哪些使用、哪些未使用,找出空闲内存分配该对象,并记录在列表中
并发因素
CAS同步
CAS+重试,保证原子性
本地线程栈分配缓存(TLAB)(堆中)
每个线程在Java堆中预先分配一小块内存,哪个线程需要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲区用完了才需要同步锁定(是否开启 TLAB 可通过 -XX:+/-UseTLAB参数来设定)
3. 内存中进行初始化,初始化为默认值
4. 对对象进行必要的设置
设置对象头
哈希吗、元数据、分代年龄、是否偏向锁等
5. 执行 init()
对象的访问定位
使用句柄
堆中划分句柄池,reference 就是存储对象的句柄地址,指向对象的引用
特点
reference中存放的是句柄池地址,对象地址改变只会影响句柄池数据,不会直接影响reference数据
直接指针
reference中直接存储对象地址,少去句柄池赚差价
访问速度快,节省一次指针定位时间
加载流程
加载
加载class文件进内存
连接
验证
确保类加载的正确性(文件格式、元数据、字节码、符号引用...)
准备
给静态变量分配内存,并赋默认值
解析
将符号引用转化为直接引用
初始化
将静态变量由默认值改为赋予实际值
使用
卸载
类加载器
种类
1. 引导类加载器/Bootstrap Classloader
加载jre/lib/下的核心类库(jvm自己的类),获取该加载器时为null,因为非Java实现
2. 扩展类加载器/Exection Classloader
加载jre/lib/ext下的扩展jar
3. 系统类加载器/Application Classloader
classpath下的类(常用:90%以上)
4. 自定义类加载器/Custom Classloader
加载自定义路径下的类
作用
将字节码加载到内存
确定类的唯一性
提供隔离性
为中间件开发者提供便利(Tomcat)
加载机制
双亲委派
概述
依赖父加载器进行加载,父加载器完不成,自己再来加载
安全,防止核心类库被修改
避免重复加载,父加载器已经加载,本身没必要再加载
加载顺序(4 -> 3 -> 2 -> 1)
打破双亲委派机制(Tomcat)
防止类冲突,资源隔离
如何打破
用户自定义类加载器,重写加载逻辑
使用的产品
Tomcat、JDBC、热部署框架
jvm指令集
javap -c
运行时数据
堆(共享)
存放对象实例(存放运行时常量+新生代+老年代)
构成
字符串常量池(java8迁移)
底层HashTable
key
字符串的hashcode值
算出值对应的index
value
字符串的引用
实例成员变量
非static修饰的变量,属于对象
线程分配缓冲区(Thread Local Allocation Buffer)
用于与线程的本地缓冲区
新生代(1/3)
eden(占80%)
s0(占10%)
s1(占10%)
老年代(2/3)
异常
不断的new对象
方法区/元空间(共享)
存放类信息、常量、静态变量和方法等类的加载信息(jdk8改为本地内存)
方法区
被虚拟机加载的类信息、常量、静态变量、即时编译器编译之后的代码等数据(class文件信息)
常量池
静态常量池(class静态描述信息)
编译生成的字面量和符号引用(类、接口、方法、字段的class静态描述信息)
运行时常量池(加载的class数据)
从静态常量池加载的数据
类、接口、方法、字段的描述信息
final修饰的变量(常量,属于类)(方法外)(方法内属于栈)
基本类型的包装类的值(-128~127)
字符串常量池(使用时)
被引用的字符串常量会被加载进来
静态(static)的变量(属于类与对象无关)
字符串常量池(java8开始移到堆上)
加载的类太多
加载了重复类
large class
加载器泄漏
栈(FIFO)(私有)
描述java方法执行的内存模型,随线程消亡无需回收
局部变量(方法内声明的变量)表
存放java方法中的局部变量(8种基本数据类型)
若变量为对象,则为对象地址
final修饰的变量(方法内)(方法外属于方法区)
操作数栈
对象做逻辑运算的临时空间
动态链接
符号引用转化为直接引用
方法出口
存储运行方法前的执行位置
栈空间不可扩展,且栈帧超过了栈的最大深度
栈溢出
栈空间可扩展,但申请不到足够的内存
本地方法栈(私有)
与虚拟机栈类似
虚拟机栈为java方法服务
本地方法栈为native方法服务
程序计数器(私有)
记录代码执行行数
线程切换时保存执行状态
唯一不会OOM,没有垃圾回收,跟随线程消亡
执行java代码时,记录执行虚拟机字节码的指令地址
执行本地方法(native),则为空
内存结构图
执行引擎
执行字节码的方式
字节码解析器
java字节码 -> C/C++ -> 机器码
模板解释器
java字节码 -> 机器码
底层原理
1. 将new()的硬编码拿过来
2. 申请内存(读、写、执行)
3. 硬编码写入该内存
4. 声明函数指针指向该内存
5. 通过该函数调用该内存
执行字节码的模式
Xint:存字节码解析器运行
Xcomp:存模板解析器运行
Xmixed:混合模式(默认)
本地库接(JNI)}
这些程序和类库可以是其它语言编写的,比如C、C++或者汇编语言。
垃圾回收
垃圾的位置
堆、方法区
程序计数器、虚拟机栈、本地方法栈:(这些用完就释放,不存在垃圾回收问题)
GC
Minor GC发生在Eden区;
span style=\
GC对应分区
什么是垃圾
算法(存在STW)
引用计数法
原理:对象中添加引用计数器,有引用+1,引用失效-1,0为垃圾
缺点:存在循环引用问题
可达性分析
原理
由起始对象节点(GC Roots)开始向下搜索,搜索走过的路径称为“引用链”,当一个对象到 GC Roots 没有任何的引用链相连时(从 GC Roots 到这个对象不可达)时,证明此对象不可用。
GC Roots
虚拟机栈(栈帧中的本地变量表)中引用的对象
本地方法栈中(Native方法)引用的对象
方法区中静态属性和常量所引用的对象
跨代引用的对象
和GCRoot 所属同一tablecard的对象
对象引用类型
强引用
只要强引用还在,垃圾回收器就不会回收
软引用
只有在内存不足时,OOM之前才会被回收
做缓存
弱引用
非必须对象,只要GC就回收
ThreadLocal对象使用弱引用防止内存泄漏
虚引用
相当于没引用,被回收时会收到通知
背景:GC只能管jvm,管不了直接内存
作用:管理直接内存,被回收前释放直接内存
原理:虚引用借助队列实现,虚引用被回收前会向队列写数据,监听队列即可接收通知
应用:netty的零拷贝
缺点
背景:单收集线程工作不会有问题,收集线程和用户线程同时工作,会出现分析错乱
1. 赋值器插入黑色到白色的新引用
2. 赋值器删除灰色到白的直接或者间接引用
后果
1. 死的对象标记为活的(可容忍,下次再回收这些浮动垃圾)
2. 活的对象标记为死的(会导致程序错误)
解决方案
CMS(增量更新)
原理:打破场景1,将所有黑色对象都标记为灰色,再扫一遍
缺点:
并发标记会存在漏标
解决方案:remark(从头重新标记)
导致STW时间很长
使用标记清除算法会产生大量内存碎片
大对象没有连续内存分配时,触发担保机制,导致和长时间的STW
G1(原始快照STAB)
原理:打破场景2,(按照当时快照场景处理)将所有删除的灰色到白色的引用记录下来,并发扫描结束后,以灰色对象为根再扫描一遍
优缺点
与CMS对比
采用标记整理算法不会产生大量内存碎片
并发标记结束后,只扫描灰色对象,STW短
STW可控,实现高吞吐量的前提下,低延迟回收垃圾
充分利用CPU,利用多核优势减少STW(会竞争CPU资源)
注意 : 最终判定
首次标记筛查
是否有必要执行finalize()方法?没必要直接回收
直接回收(没有覆盖finalize方法,或者finzlize方法已经被虚拟机调用过)
第二次标记
放入队列F-Queue,Finalizer线程去执行回收
再次标记(起死回生)
重新与引用链上的任何的一个对象建立关联。对象起死回生;譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合;
三色标记
白色
垃圾回收器没访问过(新对象)
访问过仍为白色
对象不可达
灰色
已经被垃圾回收器扫描(引用还没扫描完)
黑色
已经被垃圾回收器扫描(安全存活对象(所有引用均已扫描))
指向存活对象的其他对象均为黑色,不可能为白色
垃圾回收算法
标记清除
1. 标记(mark)
2. 清除(sweep)
不浪费内存(相对复制算法)
会产生碎片,效率低(要扫描两次)
标记整理
3. 整理存活对象,顺序排列
避免了碎片和空间占用
比复制算法效率低(维护链表使存活对象连续)
复制算法
1. 存在两个相等区域,只使用一个区域
2. 将存活对象来回copy
存活对象少时,效率高
存活对象多时,效率低
内存占用大(来回复制)
分代回收
新生代
8:1:1 eden空间不足时,触发youngGC
内存分配策略
大对象直接进老年代
减少大对象复制时内存开销
长期存活对象进入老年代
动态年龄判断尽早进入老年代
young GC后,检测到可能长期存活的对象直接进入老年代
老年代空间担保机制
young GC前风险判断,允许担保失败?否则Full GC
老年代
youngGC/fullGC
注意:内存回收
方法区(元空间)
元空间不在虚拟机内存中,使用直接内存
回收废弃常量和无用类
Full GC
垃圾收集器
分代模型
Serial(串行)
单线程、简单高效
ParNew(并行)
Serial的多线程版本,线程数与CPU核数相同,默认与CMS配合的新生代收集器
Parallel Scavenge(并行)
不支持CMS,以吞吐量优先的多线程收集器,高效利用CPU
Serial Old(串行)
Serial的老年版本,CMS的后备方案
Parallel Old(并行)
Parallel Scavenge 的老年代版本,吞吐量优先
CMS(并发)(增量更新)
主要耗时点在并发标记和重新标记,大体上用户线程和垃圾回收线程并发执行
并发收集、低停顿(STW)、注重用户体验
流程
初始标记
标记GCRoot关联的对象,STW短
并发标记
遍历GCRoot直接关联的对象,耗时久
重新标记
修正并发标记期间的错误,STW略长
并发清除
与用户线程并发,清除垃圾对象
并发阶段虽不会昂用户线程暂停,但是会与用户线程抢占CPU资源,降低吞吐量
用户线程和垃圾回收线程并发,期间会产生新垃圾,或者触发担保机制,造成长时间停顿
基于标记清除算法会产生大量内存碎片,没有连续空间存放大对象时,会导致提前FullGc
组合使用
jdk8默认(Parallel Scavenge + Parallel Old )
分区模型
G1(并发)(快照模式)(逻辑分代、物理不分区)
引入分区的概念、弱化分代概念
Region:将java堆划分为对个连续大小相等的独立区域
Humongous :专门用来存大对象(超过了一个Region容量一半)
标记GCRoot关联到的对象,暂停用户线程,STW时间较短
可达性分析找出要回收的对象,记录期间引用变动,耗时较长
最终标记
重新标记并发标记期间引用变动对象,暂停用户线程STW
筛选回收
对个Region的灰色价值和成本排序,根据用户期望的STW制定回收计划,暂停用户线程,多线程回收STW
总结
除并发标记外其他都要STW,延迟可控的情况下提高吞吐量
YoungGC
MixedGC
小内存场景CMS占优,大内存G1占优
采用标记整理,不产生垃圾碎片
利用多核优势,从分利用CPU,缩短STW
定制化处理,停顿时间可控,保证吞吐量和减小STW
内存占用高,执行负载大
维护记忆集(记忆集会记录下别的Region 指向自己的指针,并标记这些指针分别在哪些卡页的范围)
问题点
G1 STW时间怎么可控?
Garbage First, 后台维护一个优先级列表,规定时间内,优先回收最大价值的Region
Region是垃圾回收的最小单元,根据计划每次回收Region的整数倍,避免全局回收
G1停顿模型怎么预测?
region 的统计状态值越新,越能体现其回收价值
G1对象夸Region引用怎么解决?
使用记忆集,避免全局扫描GCRoot
并发标记阶段,怎么保证用户线程和回收线程互不干扰?
CMS通过增量更新
G1通过快照模式(SATB)
ZGC(JDK11)
Shenandoash(JDK12)
其他新版本GC
epsilon
PGC、C4、OpenJ9等
Parallel常用参数
CMS常用参数
G1常用参数
垃圾收集日志
默认级别(低->高)
Trace、Debug、Info、Warning、Error、Off
默认Info
GC命令
GC日志信息
查看GC基本信息
-XX:+PrintGC(JDK9之前)
-Xlog:gc(JDK9之后)
查看GC详细信息
-XX:+PrintGCDetails(JDK9之前)
-Xlog:gc*(JDK9之后)
查看GC前后各区域空间变化
-XX:+PrintHeapAtGC(JDK9之前)
-Xlog:gc+heap=debug(JDK9之后)
查看GC期间用户线程的停顿时间和并发时间
-XX:+PrintGCApplicationConcurrentTime和-XX:+PrintGCApplicationStoppedTime(JDK9之前)
-Xlog:safepoint(JDK9之后)
常看GC后剩余对象的年龄分布
-XX:+PrintTenuringDistribution(JDK9之前)
-Xlog:gc+age=trace(JDK9之后)
JVM参数设置
堆内存新生代Eden和Survivor比例
-XX:SurvivorRatio=8
内存溢出打印
-XX:+HeapDumpOnOutOfMemoryError
堆转储快照存储位置
-XX:HeapDumpPath=${目录}
元空间
设置元空间最大值
-XX:MaxMetaspaceSize(默认-1,即不受限制,只受限于本地内存大小)
指定元空间的初始空间大小
-XX:MetaspaceSize
控制GC后最小的元空间剩余容量的百分比
-XX:MinMetaspaceFreeRatio
控制GC最大的元空间剩余容量的百分比
指定直接内存的大小
-XX:MaxDirectMemorySize
0 条评论
下一页