JVM参数
- 标准参数
-version -help -cp
-
-X参数
非标准参数,也就是在JDK各个版本中可能会变动
-Xint 解释执行 -Xcomp 第一次使用就编译成本地代码 -Xmixed 混合模式,JVM自己来决定
-
-XX参数
使用得最多的参数类型,非标准化参数, 主要用于JVM调优和Debug
a.Boolean类型 格式:-XX:[+-]
+或-表示启用或者禁用name属性 比如: -XX:+UseConcMarkSweepGC 表示启用CMS类型的垃圾回收器 -XX:+UseG1GC 表示启用G1类型的垃圾回收器
b.非Boolean类型 格式:-XX
= 表示name属性的值是value 比如:-XX:MaxGCPauseMillis=500
- 其他参数(也相当于-XX参数)
-Xms1000等价于-XX:InitialHeapSize=1000 -Xmx1000等价于-XX:MaxHeapSize=1000 -Xss100等价于-XX:ThreadStackSize=100
- 查看参数
java -XX:+PrintFlagsFinal -version > flags.txt
值得注意的是”=”表示默认值,”:=”表示被用户或JVM修改后的值 ,一般要设置参数,可以先查看一下当前参数是什么,然后进行修改
- 设置参数的方式
开发工具中设置比如IDEA,eclipse 运行jar包的时候:java -XX:+UseG1GC xxx.jar web容器比如tomcat,可以在脚本中的进行设置,例如tomcat的catalina.sh 通过jinfo实时调整某个java进程的参数(参数只有被标记为manageable的flags可以被实时修改)
常用参数含义
参数 | 含义 | 说明 |
---|---|---|
-XX:CICompilerCount=3 | 最大并行编译数 | 如果设置大于1,虽然编译速度会提高,但是同样影响系 统稳定性,会增加JVM崩溃的可能 |
-XX:InitialHeapSize=100M | 初始化堆大小 | 简写-Xms100M |
-XX:MaxHeapSize=100M | 最大堆大小 | 简写-Xmx100M |
-XX:NewSize=20M | 设置年轻代的大小 | |
-XX:MaxNewSize=50M | 年轻代最大大小 | |
-XX:OldSize=50M | 设置老年代大小 | |
-XX:MetaspaceSize=50M | 设置方法区大小 | |
-XX:MaxMetaspaceSize=50M | 方法区最大大小 | |
-XX:+UseParallelGC | 使用UseParallelGC | 新生代,吞吐量优先 |
-XX:+UseParallelOldGC | 使用UseParallelOldGC | 老年代,吞吐量优先 |
-XX:+UseConcMarkSweepGC | 使用CMS | 老年代,停顿时间优先 |
-XX:+UseG1GC | 使用G1GC | 新生代,老年代,停顿时间优先 |
-XX:NewRatio | 新老生代的比值 | 比如-XX:Ratio=4,则表示新生代:老年代=1:4,也就是新 生代占整个堆内存的1/5 |
-XX:SurvivorRatio | 两个S区和Eden区的比值 | 比如-XX:SurvivorRatio=8,也就是(S0+S1):Eden=2:8, 也就是一个S占整个新生代的1/10 |
-XX:+HeapDumpOnOutOfMemoryError | 启动堆内存溢出打印 | 当JVM堆内存发生溢出时,也就是OOM,自动生成dump 文件 |
-XX:HeapDumpPath=heap.hprof | 指定堆内存溢出打印目录 | 表示在当前目录生成一个heap.hprof文件 |
XX:+PrintGCDetails - XX:+PrintGCTimeStamps - XX:+PrintGCDateStamps Xloggc:$CATALINA_HOME/logs/gc.log | 打印出GC日志 | 可以使用不同的垃圾收集器,对比查看GC情况 |
-Xss128k | 设置每个线程的堆栈大小 | 经验值是3000-5000最佳 |
-XX:MaxTenuringThreshold=6 | 提升年老代的最大临界值 | 默认值为 15 |
-XX:InitiatingHeapOccupancyPercent | 启动并发GC周期时堆内存使用占比 | G1之类的垃圾收集器用它来触发并发GC周期,基于整个堆 的使用率,而不只是某一代内存的使用比. 值为 0 则表 示”一直执行GC循环”. 默认值为 45. |
-XX:G1HeapWastePercent | 允许的浪费堆空间的占比 | 默认是10%,如果并发标记可回收的空间小于10%,则不 会触发MixedGC。 |
-XX:MaxGCPauseMillis=200ms | G1最大停顿时间 | 暂停时间不能太小,太小的话就会导致出现G1跟不上垃 圾产生的速度。最终退化成Full GC。所以对这个参数的 调优是一个持续的过程,逐步调整到最佳状态。 |
-XX:ConcGCThreads=n | 并发垃圾收集器使用的线程数量 | 默认值随JVM运行的平台不同而不同 |
-XX:G1MixedGCLiveThresholdPercent=65 | 混合垃圾回收周期中要包括的旧区域设置 占用率阈值 | 默认占用率为 65% |
-XX:G1MixedGCCountTarget=8 | 设置标记周期完成后,对存活数据上限为 G1MixedGCLIveThresholdPercent 的旧 区域执行混合垃圾回收的目标次数 | 默认8次混合垃圾回收,混合回收的目标是要控制在此目 标次数以内 |
- XX:G1OldCSetRegionThresholdPercent=1 | 描述Mixed GC时,Old Region被加入到 CSet中 | 默认情况下,G1只把10%的Old Region加入到CSet中 |
常用命令
jps
查看java进程
jps -l 查看明细
jinfo
- 查看某个java进程的name属性的值
jinfo -flag name PID
例如:
jinfo -flag MaxHeapSize PID jinfo -flag UseG1GC PID
- 修改,参数只有被标记为manageable的flags可以被实时修改
jinfo -flag [+|-] PID jinfo -flag = PID
- 查看曾经赋过值的一些参数
jinfo -flags PID
jstat
- 查看类装载信息
jstat -class PID 1000 10 查看某个java进程的类装载信息,每1000毫秒输出一次,共输出10次 ;
jstat -gc PID 1000 10 查看垃圾收集信息
jstack
jstack PID 查看线程堆栈信息
- 死锁案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class DeadLockDemo {
public static void main(String[] args)
{
DeadLock d1=new DeadLock(true);
DeadLock d2=new DeadLock(false);
Thread t1=new Thread(d1);
Thread t2=new Thread(d2);
t1.start();
t2.start();
}
}
//定义锁对象
class MyLock{
public static Object obj1=new Object();
public static Object obj2=new Object();
}
//死锁代码
class DeadLock implements Runnable {
private boolean flag;
DeadLock(boolean flag) {
this.flag = flag;
}
public void run() {
if (flag) {
while (true) {
synchronized (MyLock.obj1) {
System.out.println(Thread.currentThread().getName() + "----if 获得obj1锁");
synchronized (MyLock.obj2) {
System.out.println(Thread.currentThread().getName() + "----if获得obj2锁");
}
}
}
} else {
while (true) {
synchronized (MyLock.obj2) {
System.out.println(Thread.currentThread().getName() + "----否则获得obj2锁");
synchronized (MyLock.obj1) {
System.out.println(Thread.currentThread().getName() + "----否则获得obj1锁");
}
}
}
}
}
}
通过jstack分析,把打印信息拉到最后可以发现
jmap
- 打印出堆内存相关信息
通过idea 设置VM options -XX:+PrintFlagsFinal -Xms300M -Xmx300M 执行 jmap -heap PID
- dump出堆内存相关信息
jmap -dump:format=b,file=heap.hprof PID
- 要是在发生堆内存溢出的时候,自动dump出该文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof
常用工具
jconsole
jconsole工具是JDK自带的可视化监控工具。查看java应用程序的运行概况、监控堆信息、永久区使用情况、类加载情况等。
命令行中输入:jconsole
jvisualvm
监控本地Java进程
可以监控本地的java进程的CPU,类,线程等
监控远端Java进程
(1)在jvisualvm中选中“远程”,右击“添加” (2)主机名上写服务器的ip地址,比如31.100.39.63,然后点击“确定” (3)右击该主机“31.100.39.63”,添加“JMX”[也就是通过JMX技术具体监控远端服务器哪个Java进程] (4)要想让服务器上的tomcat被连接,需要改一下 bin/catalina.sh 这个文件
1
2
3
4
5
6
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -
Djava.rmi.server.hostname=31.100.39.63 -Dcom.sun.management.jmxremote.port=8998
-Dcom.sun.management.jmxremote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=true -
Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access -
Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password"
(5)在 ../conf 文件中添加两个文件jmxremote.access和jmxremote.password
-
jmxremote.access 文件
guest readonly manager readwrite
-
jmxremote.password 文件
guest guest manager manager
注:授予权限 : chmod 600 jmxremot
Arthas
Arthas 是Alibaba开源的Java诊断工具,采用命令行交互模式,是排查jvm相关问题的利器。
-
下载安装
curl -O https://alibaba.github.io/arthas/arthas-boot.jar java -jar arthas-boot.jar
然后可以选择一个Java进程
-
常用命令
version:查看arthas版本号 help:查看命名帮助信息 cls:清空屏幕 session:查看当前会话信息 quit:退出arthas客户端 --- dashboard:当前进程的实时数据面板 thread:当前JVM的线程堆栈信息 jvm:查看当前JVM的信息 sysprop:查看JVM的系统属性 --- sc:查看JVM已经加载的类信息 dump:dump已经加载类的byte code到特定目录 jad:反编译指定已加载类的源码 --- monitor:方法执行监控 watch:方法执行数据观测 trace:方法内部调用路径,并输出方法路径上的每个节点上耗时 stack:输出当前方法被调用的调用路径
MAT
Java堆分析器,用于查找内存泄漏 Heap Dump,称为堆转储文件,是Java进程在某个时间内的快照
-
获取Dump文件
手动 : jmap -dump:format=b,file=heap.hprof PID
自动 : -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof
-
使用
- Histogram
Histogram可以列出内存中的对象,对象的个数及其大小
Class Name:类名称,java类名 Objects:类的对象的数量,这个对象被创建了多少个 Shallow Heap:一个对象内存的消耗大小,不包含对其他对象的引用 Retained Heap:是shallow Heap的总和,即该对象被GC之后所能回收到内存的总和
右击类名—>List Objects—>with incoming references—>列出该类的实例
右击Java对象名—>Merge Shortest Paths to GC Roots—>exclude all …—>找到GCRoot以及原因
- Leak Suspects
查找并分析内存泄漏的可能原因
Reports—>Leak Suspects—>Details
- Top Consumers
列出大对象
GC日志分析工具
拿到GC日志文件
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:gc.log
在线工具:http://gceasy.io
GCViewer
GC优化分析
垃圾收集发生的时机
一般以下几种情况会发生垃圾回收
(1)当Eden区或者S区不够用了 (2)老年代空间不够用了 (3)方法区空间不够用了 (4)System.gc()
System.gc()只是通知要回收,什么时候回收由JVM决定。 但是不建议手动调用该方法,因为消耗的资源比较大。
GC日志文件分析
- 使用gceasy
- GCViewer
G1调优与最佳指南
是否选用G1垃圾收集器的判断依据
(1)50%以上的堆被存活对象占用 (2)对象分配和晋升的速度变化非常大 (3)垃圾回收时间比较长
- 使用G1GC垃圾收集器: -XX:+UseG1GC
修改配置参数,获取到gc日志,使用GCViewer分析吞吐量和响应时间
- 调整内存大小再获取gc日志分析
-XX:MetaspaceSize=100M -Xms300M -Xmx300M
比如设置堆内存的大小,获取到gc日志,使用GCViewer分析吞吐量和响应时间
- 调整最大停顿时间
-XX:MaxGCPauseMillis=20 设置最大GC停顿时间指标
比如设置最大停顿时间,获取到gc日志,使用GCViewer分析吞吐量和响应时间
- 启动并发GC时堆内存占用百分比
-XX:InitiatingHeapOccupancyPercent=45 G1用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比例。值为 0 则表示“一直执行GC循环)’. 默认值为 45 (例如, 全部的 45% 或者使用了45%).
比如设置该百分比参数,获取到gc日志,使用GCViewer分析吞吐量和响应时间
最佳指南
官网建议 :https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#recommendations
(1)不要手动设置新生代和老年代的大小,只要设置整个堆的大小
G1收集器在运行过程中,会自己调整新生代和老年代的大小 其实是通过adapt代的大小来调整对象晋升的速度和年龄,从而达到为收集器设置的暂停时间目标 如果手动设置了大小就意味着放弃了G1的自动调优
(2)不断调优暂停时间目标
一般情况下这个值设置到100ms或者200ms都是可以的(不同情况下会不一样),但如果设置成50ms就不太合理。暂停时间设置的太短,就会导致出现G1跟不上垃圾产生的速度。最终退化成Full GC。所以对这个参数的调优是一个持续的过程,逐步调整到最佳状态。暂停时间只是一个目标,并不能总是得到满足。
(3)使用-XX:ConcGCThreads=n来增加标记线程的数量
IHOP如果阀值设置过高,可能会遇到转移失败的风险,比如对象进行转移时空间不足。如果阀值设置过低,就会使标记周期运行过于频繁,并且有可能混合收集期回收不到空间。 IHOP值如果设置合理,但是在并发周期时间过长时,可以尝试增加并发线程数,调高ConcGCThreads。
(4)适当增加堆内存大小
JVM性能优化思路
常见问题汇总
(1)内存泄漏与内存溢出的区别 内存泄漏:对象无法得到及时的回收,持续占用内存空间,从而造成内存空间的浪费。 内存溢出:内存泄漏到一定的程度就会导致内存溢出,但是内存溢出也有可能是大对象导致的。 (2)young gc会有stw吗? 不管什么 GC,都会有 stop-the-world,只是发生时间的长短。 (3)major gc和full gc的区别 major gc指的是老年代的gc,而full gc等于young+old+metaspace的gc。 (4)G1与CMS的区别是什么 CMS 用于老年代的回收,而 G1 用于新生代和老年代的回收。 G1 使用了 Region 方式对堆内存进行了划分,且基于标记整理算法实现,整体减少了垃圾碎片的产生。 (5)什么是直接内存 直接内存是在java堆外的、直接向系统申请的内存空间。通常访问直接内存的速度会优于Java堆。因此出于性能的考虑,读写频繁的场合可能会考虑使用直接内存。 (6)不可达的对象一定要被回收吗?
即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。 被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关 联,否则就会被真的回收。 (7)方法区中的无用类回收方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢?
判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面 3 个条件才能算是无用的类 :
- 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
- 加载该类的 ClassLoader 已经被回收。
- 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。 (8)不同的引用
JDK1.2以后,Java对引用进行了扩充:强引用、软引用、弱引用和虚引用