一,JVM参数设置以及日志分析
1,JVM的参数设置
1.1,标准参数选项
特点就是比较稳定,后续的版本也不会变化,以-开头。可以打开cmd可执行命令,输入java -help
,就可以查看对应的命令,如可以通过-server和-client模式设置是客户端模式还是服务端模式
java -help
1.2,-X参数选项
非标准化参数,功能相对稳定,后续版本可能会发生变动,以-X开头。可以输入java -X
,就可以查看到对应的命令。如可以通过这个-Xmixed来作为执行引擎的混合模式,解释器+编译器。
java -X
还有一些如设置堆的初始大小,最大大小以及设置线程的大小等
-Xms10m -Xmx10m -Xss10m
1.3,-XX参数类型
也是非标准化参数,使用的自多的参数类型,功能相对不稳定,以-XX开头,一般用于开发和调试JVM。
又可以对这些指令进行分类,分成boolean类型和非boolean类型。boolean类型的通过+、- 加或者减来启用或者禁用某个属性,因为有的指令是默认开启或者默认关闭的。如下面的一些命令:
-XX:+UseParallelGc 选择垃圾收集器为并行垃圾收集器 -XX:+UseG1GC 启用G1收集器 -XX:+UseAdaptiveSizePolicy 自动选择新生代大小和相应的s区的比例 -XX:+PrintFlagsFinal 输出所有参数的名称和默认值
非boolean类型的如下
//key/value类型 -XX:NewSize=1024M 新生代初始大小 -XX:MaxGCPauseMillis=500 设置GC停顿时间:500ms -XX:GCTimeRatio=19 设置吞吐量 -XX:NewRatio=2 设置新生代和老年代比例
2,添加JVM参数
如在运行一个jar包,可以设置堆的大小,gc的间隔以及打印日志等
java -Xms1024m -Xmx1024m -XX:PrintGcDetails -XX:+PrintGcTimeStamps -jar xx.jar
3,常用的JVM的参数选项
打印的相关参数有
-XX:PrintFlagsFinal 表示打印出XX选项在运行时程序生效的值 -XX:PrintFlagsInitial 表示打印出XX选项的默认值 -XX:PrintVMOptions 打印JVM参数
栈的相关参数有
-Xss1m 设置每个线程栈的大小为1m
堆内存的相关参数
-Xms1024m -Xmx1024m 设置堆的初始大小和最大大小都是1024m -Xmn2g 设置的是年轻代的大小 -XX:NewRatio=4 设置老年代和新生代的比例,默认为2 -XX:NewSize=1g 设置年轻代的初始大小为1g -XX:NewMaxSize=1g 设置年轻代的最大大小为1g
在堆中还有一个重要的参数设置,这个自动选择各区比例是开启的,所有有时查看到的eden区和s区的比例有时为6:1,这个比例是动态调整的,如果想让他的值为默认值8:1,就得将这个-XX:SurvivorRatio=8
开启
-XX:SurvivorRatio=8 设置Eden区和s区的比例,默认为8 -XX:+UseAdaptiveSizePolicy 自动选择各区比例
方法区的相关参数,由于jdk8以及之后实现这个方法区的方式都是元空间,因此只谈元空间的参数设置
-XX:MetaspaceSize 初始空间 -XX:MaxMetaspaceSize=8 最大空间 -XX:+UseCompressedOops 压缩对象 指针 -XX:CompressedClassSpaceSize 设置类的元空间大小,默认是1G
OutofMemory相关参数
-XX:+HeapDumpOnOutOfMemory 表示出现OOM时,生成一个堆的dump文件 -XX:HeapDumpBeforeFullGc 表示出现这个FullGc之前,生成heap的存储文件 -XX:HeapDumpPath=<path> 指定heap转存储文件的存储路径 -XX:OnOutOfMemoryError 指定一个可行性的程序或者脚本
垃圾收集相关选项
-XX:+UseSerialGC 指定新生代和老年代使用serial回收器 -XX:+UseParNewGC 手动指定ParNewGC作为新生代的回收器 -XX:ParallelGCThreads 限制线程数量,默认开启和CPU相同的线程数 -XX:+UseParallelGC 手动指定并使用Parallel作为并行收集器 -XX:+UseParallelOldGC 手动指定老年代都是使用并行回收器 -XX:GCTimeRatio 垃圾收集时间占总时间比例 -XX:+UseAdapaiveSizePolicy 自适应调节策略 -XX:+UseConcMarkSweepGC 手动指定CMS作为垃圾回收器 -XX:ParallelCMSThreads 设置CMS的线程数量 -XX:MaxGCPauseMillis 设置期望值达到的最大GC停顿时间 -XX:ParallelGCThread 设置STW时GC线程数的值,最多设置为8 -XX:UseG1GC 手动指定使用G1收集器执行内存回收任务 -XX:G1HeapRegionSize 设置每个region的分区大小 -XX:ConcGCThreads 设置并发标记的线程数
GC日志相关选项
-verbose:gc 输出GC日志信息 -XX:+PrintGC 输出GC日志信息 -XX:+PrintGCDetails 发生垃圾回收时打印内存回收的详细信息 -XX:+PrintHeapAtGC 每一次GC前和GC后,都打印堆信息 -XX:PrintGCTimeStamps 输出gc打印时间戳信息 -Xloggc <path> 将日志文件保存到指定的path路径下
其他参数
-XX:+DisableExplicitGC 禁用虚拟机执行System.gc() -XX:+UseCodeCacheFlushing 清理一些编译的代码 -XX:+DoEscapeAnalysis 开启逃逸分析 -XX:+UseBiasedLocking 开启偏向锁 -XX:+UseTLAB 使用TLAB,默认是打开的 -XX:TLABSize 设置TLAB的大小
也可以通过java代码来获取jvm参数
/** * @author zhenghuisheng * @date : 2023/5/19 */ public class JvmParamTest { public static void main(String[] args) { MemoryMXBean memorymbean = ManagementFactory.getMemoryMXBean(); MemoryUsage usage = memorymbean.getHeapMemoryUsage(); System.out.println("INIT HEAP: " + usage.getInit()); System.out.println("MAX HEAP: " + usage.getMax()); System.out.println("USE HEAP: " + usage.getUsed()); System.out.println("\nFull Information:"); System.out.println("Heap Memory Usage: " + memorymbean.getHeapMemoryUsage()); System.out.println("Non-Heap Memory Usage: " + memorymbean.getNonHeapMemoryUsage()); List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments(); System.out.println("===================java optiOns=============== "); System.out.println(inputArguments); System.out.println("=======================通过java来获取相关系统状态============================ "); int i = (int)Runtime.getRuntime().totalMemory()/1024;//Java 虚拟机中的内存总量,以字节为单位 System.out.println("总的内存量 i is "+i); int j = (int)Runtime.getRuntime().freeMemory()/1024;//Java 虚拟机中的空闲内存量 System.out.println("空闲内存量 j is "+j); System.out.println("最大内存量 is "+Runtime.getRuntime().maxMemory()/1024); System.out.println("=======================OperatingSystemMXBean============================ "); OperatingSystemMXBean osm = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); //获取操作系统相关信息 System.out.println("osm.getArch() "+osm.getArch()); System.out.println("osm.getAvailableProcessors() "+osm.getAvailableProcessors()); //System.out.println("osm.getCommittedVirtualMemorySize() "+osm.getCommittedVirtualMemorySize()); System.out.println("osm.getName() "+osm.getName()); //System.out.println("osm.getProcessCpuTime() "+osm.getProcessCpuTime()); System.out.println("osm.getVersion() "+osm.getVersion()); //获取整个虚拟机内存使用情况 System.out.println("=======================MemoryMXBean============================ "); MemoryMXBean mm=(MemoryMXBean)ManagementFactory.getMemoryMXBean(); System.out.println("getHeapMemoryUsage "+mm.getHeapMemoryUsage()); System.out.println("getNonHeapMemoryUsage "+mm.getNonHeapMemoryUsage()); //获取各个线程的各种状态,CPU 占用情况,以及整个系统中的线程状况 System.out.println("=======================ThreadMXBean============================ "); ThreadMXBean tm=(ThreadMXBean)ManagementFactory.getThreadMXBean(); System.out.println("getThreadCount "+tm.getThreadCount()); System.out.println("getPeakThreadCount "+tm.getPeakThreadCount()); System.out.println("getCurrentThreadCpuTime "+tm.getCurrentThreadCpuTime()); System.out.println("getDaemonThreadCount "+tm.getDaemonThreadCount()); System.out.println("getCurrentThreadUserTime "+tm.getCurrentThreadUserTime()); //当前编译器情况 System.out.println("=======================CompilatiOnMXBean============================ "); CompilationMXBean gm=(CompilationMXBean)ManagementFactory.getCompilationMXBean(); System.out.println("getName "+gm.getName()); System.out.println("getTotalCompilationTime "+gm.getTotalCompilationTime()); //获取运行时信息 System.out.println("=======================RuntimeMXBean============================ "); RuntimeMXBean rmb=(RuntimeMXBean)ManagementFactory.getRuntimeMXBean(); System.out.println("getClassPath "+rmb.getClassPath()); System.out.println("getLibraryPath "+rmb.getLibraryPath()); System.out.println("getVmVersion "+rmb.getVmVersion()); } }
其打印结果如下
INIT HEAP: 268435456 MAX HEAP: 3799515136 USE HEAP: 8061840 Full Information: Heap Memory Usage: init = 268435456(262144K) used = 8061840(7872K) committed = 257425408(251392K) max = 3799515136(3710464K) Non-Heap Memory Usage: init = 2555904(2496K) used = 5345064(5219K) committed = 8060928(7872K) max = -1(-1K) ===================java optiOns=============== [-javaagent:D:\idea\IntelliJ IDEA 2019.1\lib\idea_rt.jar=58982:D:\idea\IntelliJ IDEA 2019.1\bin, -Dfile.encoding=UTF-8] =======================通过java来获取相关系统状态============================ 总的内存量 i is 251392 空闲内存量 j is 243519 最大内存量 is 3710464 =======================OperatingSystemMXBean============================ osm.getArch() amd64 osm.getAvailableProcessors() 4 osm.getName() Windows 10 osm.getVersion() 10.0 =======================MemoryMXBean============================ getHeapMemoryUsage init = 268435456(262144K) used = 8061840(7872K) committed = 257425408(251392K) max = 3799515136(3710464K) getNonHeapMemoryUsage init = 2555904(2496K) used = 5396384(5269K) committed = 8060928(7872K) max = -1(-1K) =======================ThreadMXBean============================ getThreadCount 6 getPeakThreadCount 6 getCurrentThreadCpuTime 375000000 getDaemonThreadCount 5 getCurrentThreadUserTime 187500000 =======================CompilatiOnMXBean============================ getName HotSpot 64-Bit Tiered Compilers getTotalCompilationTime 28 =======================RuntimeMXBean============================
二,GC日志的分析
GC按照回收区域主要分为两大类型:一种是部分收集,一种是整堆收集。 部分收集的意思就是不是完整收集整个java堆的垃圾收集,比如有新生代的Minor GC、老年代的MajorGC;整堆收集就是收集整个Java堆和方法区的垃圾收集,比如有Full GC。
触发Full GC的场景如下:老年代的空间不足、方法区的空间不足、显式调用System.GC()、Minor GC进入老年代的平均大小大于老年代的可用内存大小、大对象直接进入老年代
在GC中,一般会有三个时间:user、sys、real。
user表示的是用户态所使用的时间,这是执行此进程所使用的实际CPU时间。
sys表示的是内核态消耗的时间,即在内核系统调用或等待系统事件所使用的CPU时间
real是程序开始到结束的时间。
如下面是一段线上服务的GC日志信息
Heap: PSYoungGen total 1560576K, used 202793K [0x0000000755580000, 0x00000007c0000000, 0x00000007c0000000) eden space 1376768K, 13% used [0x0000000755580000,0x000000076115da18,0x00000007a9600000) from space 183808K, 5% used [0x00000007b4c80000,0x00000007b56accb0,0x00000007c0000000) to space 185344K, 0% used [0x00000007a9600000,0x00000007a9600000,0x00000007b4b00000) ParOldGen total 3495424K, used 80041K [0x0000000680000000, 0x0000000755580000, 0x0000000755580000) object space 3495424K, 2% used [0x0000000680000000,0x0000000684e2a410,0x0000000755580000) Metaspace used 93746K, capacity 98782K, committed 99608K, reserved 1136640K class space used 11338K, capacity 12190K, committed 12328K, reserved 1048576K
GC日志的格式规律一般都是:GC前内存占用 —> GC后内存占用(总内存)
[PSYoungGen:5986K -> 696K(8704K)] 5986K -> 704K(9216K)
GC失败的场景
[GC (Allocation Failure) [PSYoungGen: 2240K->384K(2560K)] 4803K->2947K(9728K), 0.0006347 secs]
也可以使用一些GC工具进行日志分析,如GC Easy,GC View工具等等。如下面是一个GC Easy的官网,其地址为:https://gceasy.io/,选择对应的log文件即可
在选择文件之后,就可以进入到下面的页面,从上往下滑就可以查看对应的详细信息
在all Thread这里,可以发现这现线程的状态,正处于阻塞状态
总而言之,这款工具还是挺好使用的