如何开启jvm⽇志_Java从⼩⽩到⼤⽜,JVM不得不知的⼀些参
数和配置
神秘的 JVM 码
有的同学虽然写了⼀段时间 Java 了,但是对于 JVM 却不太关注。有的同学说,参数都是团队规定好的,部署的时候也不⽤我动⼿,关注它有什么⽤,⽽且,JVM 这东西,听上去就感觉很神秘很⾼深的样⼦,还是算了吧。
没错,部署的时候可能⽤不到你亲⾃动⼿,但是出现问题了怎么办,难道不⽤你解决问题吗,如果对 JVM 了解不够的话,有些问题可能排查起来就很费⼒,或者根本⽆法解决。
本篇以 JDK Hotspot 8 为背景,介绍⼀下 JVM 的常⽤参数。建议你在做⼀些⼩项⽬、⼩ demo 的时候,也把这些参数加上,加深印象。以我的经验来看,有些知识你刚开始接触的时候会感觉很难理解,但是没关系,万事开头难嘛,知识点都是需要消化时间的。第⼀天不理解,甚⾄过了⼀个⽉也不理解,但是总有那么⼀刻,你会突然有种茅塞顿开的感觉,感觉⼀下⼦通了。最后⼼⾥⾯感谢⾃⼰在多少多少天以前能够开始学习并坚持学习这些知识点。
只介绍⼀些常⽤参数,除了这些常⽤参数外,Hotspot 还提供了很多其他的参数,每⼀个都值得考究。
还是要把内存模型图放在这⾥,⽅便理解。
image
-Xmx: 堆的最⼤值,例如 -Xmx2048M,允许最⼤堆内存 2G---Xms: 堆的初始值,例如 -Xms2048M,初始堆⼤⼩为 2G-Xmx
堆参数:-Xms
-XX:SurvivorRatio:Eden 区所占⽐例,默认是 8,也就是 80%,例如 -XX:SurvivorRatio=8
Xmn: 新⽣代⼤⼩-XX:SurvivorRatio
Xmn
image
最好将 -Xms 和 -Xmx 的值设置成⼀样的值,这样做是为了防⽌随着堆空间使⽤量增加,会动态的调整堆空间⼤⼩,有⼀定的性能损耗,
不如开始就设置成相同的值,来规避性能损失。
-Xss:栈空间⼤⼩,栈是线程独占的,所以是⼀个线程使⽤栈空间的⼤⼩,例如 -Xss256K,如果不设置此参数,默认值是 1M,
栈参数-Xss
⼀般来讲设置成 256K 就⾜够了。
Metaspace 参数-XX:MetaspaceSize
-XX:MetaspaceSize:Metaspace 空间初始⼤⼩,如果不设置的话,默认是20.79M,这个初始⼤⼩是触发⾸次
-XX:MaxMetaspaceSize:Metaspace 最⼤值,默认不限制⼤⼩,Metaspace Full GC 的阈值,例如 -XX:MetaspaceSize=256M-XX:MaxMetaspaceSize
但是线上环境建议设置,例如
-XX:MinMetaspaceFreeRatio:最⼩空闲⽐,当 Metaspace 发⽣ GC 后,会计算 Metaspace 的
-XX:MaxMetaspaceSize=256M-XX:MinMetaspaceFreeRatio
空闲⽐,如果空闲⽐(空闲空间/当前 Metaspace ⼤⼩)⼩于此值,就会触发 Metaspace 扩容。默认值是 40 ,也就是 40%,例如 -
-XX:MaxMetaspaceFreeRatio:最⼤空闲⽐,当 Metaspace 发⽣ GC 后,会计算 Metaspace 的
XX:MinMetaspaceFreeRatio=40-XX:MaxMetaspaceFreeRatio
空闲⽐,如果空闲⽐(空闲空间/当前 Metaspace ⼤⼩)⼤于此值,就会触发 Metaspace 释放空间。默认值是 70 ,也就是 70%,例如 -
小白学java有前途吗
XX:MaxMetaspaceFreeRatio=70
建议将 MetaspaceSize 和 MaxMetaspaceSize 设置为同样⼤⼩,避免频繁扩容。
简单⽇志-verbose:gc 或者 -XX:+PrintGC
GC ⽇志简单⽇志
⽇志格式:
[GC (Allocation Failure)  7892K->5646K(19456K), 0.0060442 secs]
[GC (Allocation Failure) , 0.0066315 secs]
[Full GC (Allocation Failure)  19302K->13646K(19456K), 0.0032698 secs]
详细⽇志
#打印详细⽇志
-XX:+PrintGCDetails
#打印 GC 的时间点
-XX:+PrintGCDateStamps
⽇志格式
2019-11-13T14:06:46.099-0800: [GC (Allocation Failure) 2019-11-13T14:06:46.099-0800: [DefNew (promotion failed) : 9180K->9157K(9216K), 0.008429
2019-11-13T14:06:47.243-0800: [Full GC (Allocation Failure) 2019-11-13T14:06:47.244-0800: [Tenured: 10145K->10145K(10240K), 0.0042686 secs] 19304K->以下⼏个 GC ⽇志相关的参数打印的内容⽐较多,⽣产环境可选择性开启,⼤多数时候不需要开启。
GC 前后的堆信息
-XX:+PrintHeapAtGC
{Heap before GC invocations=0 (full 0):
def new generation  total 9216K, used 7892K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
eden space 8192K,  96% used [0x00000007bec00000, 0x00000007bf3b5200,
<
class space    used 445K, capacity 462K, committed 512K, reserved 1048576K
Heap after GC invocations=1 (full 0):
def new generation  total 9216K, used 1023K [0x00000007bec00000,
<
Metaspace      used 3892K, capacity 4646K, committed 4864K, reserved 1056768K
class space    used 445K, capacity 462K, committed 512K, reserved 1048576K
}
GC 导致的 Stop the world 时间
-XX:+PrintGCApplicationStoppedTime
Total time for which application threads were stopped: 0.0070384 seconds, Stopping threads took: 0.0000200 seconds
加载类信息
-verbose:class
[Loaded java.URLClassLoader$3$1 from /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/rt.jar]
GC 前后的类加载情况
-XX:+PrintClassHistogramBeforeFullGC
-XX:+PrintClassHistogramAfterFullGC
num    #instances        #bytes  class name
----------------------------------------------
1:          140      19016264  [B
2:          2853        226256  [C
3:          138        169072  [I
4:          761          86240  java.lang.Class
5:          2850          68400  java.lang.String
6:          660          41024  [Ljava.lang.Object;
⽇志输出到⽂件
以上参数配置好之后,默认会输出到控制台或者服务指定的统⼀⽇志的位置。但是这⾥还会有服务的⼀般性信息⽇志、错误⽇志等,都混在⼀起的话会⽐较乱,所以,⼀般都会把 jvm ⽇志单独存放。
#GC 活动⽇志,根据配置的参数输出内容
-Xloggc:/Users/fengzheng/jvmlog/gc.log
`#致命错误⽇志,只有在 jvm 发⽣崩溃的时候会输出
-XX:ErrorFile=/Users/fengzheng/jvmlog/hs_err_pid%p.log
堆溢出现场保留
有些错误虽然不会导致 jvm 崩溃,但是对于服务⽽⾔也是⾮常严重的,⽐如stackOverflow、OutOfMemoryError,发⽣错误后,留存现场信息对分析错误原因是⾄关重要的。jvm 提供了保留堆溢出现场的⽅法,对于 JDK 8 ⽽⾔,可能是 heap 溢出,也可能是 Metasapce 溢出。
-
XX:HeapDumpPath=/Users/fengzheng/jvmlog
-XX:+HeapDumpOnOutOfMemoryError
最后出现异常后,保存的⽂件格式为 java_pidxxx.hprof,pid 后⾯是发⽣溢出的进程 id,之后可以⽤ VisualVM、JProfiler 等⼯具打开分析。
设置垃圾回收器类型
随着 JDK 版本的升级,可使⽤的垃圾收集器类型也越来越多了。JDK 8 可使⽤的垃圾收集器有 7 种,当然有点只适⽤于年轻代,有点只使⽤于⽼年代,JDK 8 中最新的垃圾收集器是 G1,可以⽤于年轻代和⽼年代。到了 JDK 11,还出了 ZGC。
下图是 JDK 8 中可使⽤的垃圾收集器以及它们配合使⽤的关系。
image
Serial、ParNew、Parallel Scavenge 只适⽤于年轻代,CMS、Serial Old、Parallel Old 只适⽤于⽼年代,⽽ G1 通⽤于年轻代和⽼年代。连线表⽰它们之间可配合使⽤的关系,其中 CMS 和 Serial Old 连线的意思是说 Serial Old 会作为 CMS 的后预案,当 CMS 发⽣Concurrent Mode Failure 时启⽤。
在 JDK 8 中,如果不指定垃圾收集器,默认使⽤参数 -XX:+UseParallelGC,新⽣代使⽤ Parallel Scavenge,⽼年代使⽤ Serial Old。--
-XX:+UseConcMarkSweepGC:使⽤
XX:+UseSerialGC
XX:+UseSerialGC:使⽤ Serial + Serial Old ,运⾏于 client 模式下的默认设置-XX:+UseConcMarkSweepGC
-XX:+UseParallelGC:Parallel Scavenge + Serial Old,JDK 8 server 模式下的默认ParNew+CMS+Serial Old,CMS 垃圾收集器-XX:+UseParallelGC
-XX:+UseG1GC:使⽤ G1 垃圾收集器
设置-XX:+UseParallelOldGC
-XX:+UseParallelOldGC:Parallel Scavenge + Parallel Old-XX:+UseG1GC
开启远程 JMX 监控
除了⽇志外,当我们需要实时查看 JVM 运⾏情况的时候怎么办,当然可以到 JVM 所在服务器⽤ jstack、jmap、jinfo 等⼯具进⾏查看,但是⼜不够直观,这时候就需要开启 JMX 远程功能,使⽤ jConsole、VisualVM 等⼯具进⾏监控。或者⾃⼰开发监控平台,⽐如我之前就做了⼀个 web 版的简易 VisualVm。
开启参数如下:
-Dcom.sun.management.jmxremote
#指定 jvm 所在服务器 ip 或域名
-i.server.hostname=192.168.1.1
#指定端⼝
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.i.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-
Dcom.sun.management.jmxremote.ssl=false
最后
学习 Java ⼀段时间之后,想要进⼀步提升,JVM 是你永远绕不过去的,JVM 如何⼯作的,如何调优,将决定你和其他开发⼈员的差距。别等了,赶紧学吧。