通过idea64.exe.vmoptions了解一些JVM参数

烟雨 6年前 (2020-06-23) 阅读数 1416 #Idea
文章标签 IdeaJava

下面是个人使用的Idea2020.1.2的配置idea64.exe.vmoptions文件内容

-server
-Xms512m
-Xmx1024m
-Xmn393m
-Xverify:none
-ea
-XX:+AlwaysPreTouch
-XX:MaxGCPauseMillis=100
-XX:+UseConcMarkSweepGC
-XX:ParallelGCThreads=4
-XX:+CMSParallelRemarkEnabled
-XX:ConcGCThreads=4
-XX:CICompilerCount=4
-XX:ReservedCodeCacheSize=240m
-XX:+TieredCompilation
-XX:+UseCompressedOops
-XX:SoftRefLRUPolicyMSPerMB=50
-XX:MaxTenuringThreshold=10
-XX:-OmitStackTraceInFastThrow
-Dsun.io.useCanonCaches=false
-Dsun.io.useCanonPrefixCache=false
# 设置禁用IPv6
-Djava.net.preferIPv4Stack=true
-Djdk.http.auth.tunneling.disabledSchemes=""
-Djdk.attach.allowAttachSelf
-Dkotlinx.coroutines.debug=on
-Djdk.module.illegalAccess.silent=true

一、-server

JVM的参数配置分别是 服务器模式(-server) 和 客户端模式(client)
比如垃圾回收机制,客户端模式下,要求的是用户体验流程,无明显滞留感(就是没有卡的现象)。而服务端,要求的是吞吐量,就是单位时间内执行的代码要求越多越好。

二、-Xmx1024m -Xms512m -Xmn393m -Xverify:none

-Xmx1024m设置JVM最大可用内存为1024m。
-Xms512m:设置JVM初始内存为512m(启动时占用内存大小)。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn393m:设置JVM年轻代(Young Generation)大小为393m。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小 。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k设置每个线程的堆栈大小为128k。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内 存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-Xverify:none:关闭Java字节码验证,从而加快了类装入的速度,并使得在仅为验证目的而启动的过程中无需装入类,缩短了启动时间。 
-ea:启动断言检查机制。

三、-XX:+AlwaysPreTouch

作用:服务启动的时候分配真实的物理内存给JVM。
JAVA进程启动的时候,虽然我们可以为JVM指定合适的内存大小,但是这些内存操作系统并没有真正的分配给JVM,而是等JVM访问这些内存的时候,才真正分配。这样会造成以下问题:
  1. 第1次YGC(年轻带GC)之前Eden区分配对象的速度较慢;

  2. YGC(年轻带GC)的时候,Young区的对象要晋升到Old区的时候,这个时候需要操作系统真正分配内存,这样就会加大YGC(年轻带GC)的停顿时间。

配置-XX:+AlwaysPreTouch参数可以优化这个问题,不过这个参数也有副作用,它会影响启动时间。
[root@node2 afei]# date; java -Xmx16G -Xms16G -XX:+AlwaysPreTouch Test; dateTue Jan 15 14:31:59 CST 2019
Hello World!Tue Jan 15 14:32:35 CST 2019[root@node2 afei]# date; java -Xmx16G -Xms16G -XX:-AlwaysPreTouch Test; dateTue Jan 15 14:33:55 CST 2019
Hello World!Tue Jan 15 14:33:55 CST 2019[root@node2 afei]# date; java -Xmx8G -Xms8G -XX:+AlwaysPreTouch Test; dateTue Jan 15 14:32:53 CST 2019
Hello World!Tue Jan 15 14:33:13 CST 2019[root@node2 afei]# date; java -Xmx8G -Xms8G -XX:-AlwaysPreTouch Test; dateTue Jan 15 14:36:48 CST 2019
Hello World!Tue Jan 15 14:36:48 CST 2019

对比结果
~ -XX:+AlwaysPreTouch XX:-AlwaysPreTouch(default)
16G 36s <1s
8G 20s <1s

3.1、慢的原因

配置这个参数后这么耗时其中一个原因是,这个特性在JDK8版本以前都不是并行处理的,到了JDK9才是并行。
可查看如下链接了解Parallelize Memory Pretouch: https://bugs.openjdk.java.net/browse/JDK-8157952

3.2、根本原因

在没有配置-XX:+AlwaysPreTouch参数即默认情况下,JVM参数-Xms申明的堆只是在虚拟内存中分配,而不是在物理内存中分配:它被以一种内部数据结构的形式记录,从而避免被其他进程使用这些内存。这些内存页直到被访问时,才会在物理内存中分配。当JVM需要内存的时候,操作系统将根据需要分配内存页。
配置-XX:+AlwaysPreTouch参数后,JVM将-Xms指定的堆内存中每个字节都写入"0",这样的话,除了在虚拟内存中以内部数据结构保留之外,还会在物理内存中分配内存。并且由于touch(分配物理内存)这个行为是单线程的,因此它将会让JVM进程启动变慢。所以,要么选择减少接下来对每个缓存页的第一次访问时间,要么选择减少JVM进程启动时间,这是一种权衡。

四、-XX:MaxGCPauseMillis=100

设置每次年轻代垃圾回收的最长时间(毫秒单位),如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。

五、-XX:+UseConcMarkSweepGC -XX:ParallelGCThreads=4 -XX:+CMSParallelRemarkEnabled

-XX:+UseConcMarkSweepGC:使用并行并发CMS垃圾回收器。测试中配置这个以后,-XX:NewRatio=4的配置失效了。所以,此时年轻代大小最好用-Xmn设置。
-XX:ParallelGCThreads=4:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
-XX:+CMSParallelRemarkEnabled:降低标记停顿(不知道有啥用)

六、-XX:ConcGCThreads=4

设置并发线程数量为4个

CMS默认启动的并发线程数是(ParallelGCThreads+3)/4。

当有4个并行线程时,有1个并发线程,当有5~8个并行线程时,有2个并发线程。

七、-XX:ReservedCodeCacheSize=240m

设置编译器缓存大小为240m

八、-XX:+TieredCompilation

通常情况下,Java程序最初都是被编译为字节码,通过解释器进行解释执行,解释执行能够获得更好的启动时间。某些被频繁执行的方法或者代码块,会被JVM认定为“热点代码”。在运行时JVM会把这些热点代码编译成与本地平台相关的机器码,并且进行各种层次的优化,以提高执行效率。完成这个任务的编译器称为即时编译器(JIT编译器)。

8.1、HotSpot的分层编译模式

HotSpot内置了C1编译器和C2编译器。默认情况下,JVM采取解释器和其中一个编译器直接配合的运行模式,编译器的选择,根据自身的版本以及宿主机器的硬件性能自动选择。此外,用户也可以通过JVM参数强制JVM的运行模式。如下表:
参数 运行模式 说明
-Xint 解释模式 编译器不介入工作,所有代码都使用解释器解释执行
-Xcomp 编译模式 优先采用编译方式执行,但是在编译无法进行的情况下,还是会进行解释执行
-client 混合模式(Client模式) 解释器搭配C1的混合模式,适用于对于执行时间较短的,或者对启动性能有要求的程序
-server 混合模式(Server模式) 解释器搭配C2的混合模式,适用于执行时间较长的,或者对峰值性能有要求的程序

8.2、分层编译

Java 7 引入了分层编译(对应参数 -XX:+TieredCompilation)的概念,综合了 C1 的启动性能优势和 C2 的峰值性能优势。
分层编译将 Java 虚拟机的执行状态分为了五个层次。为了方便阐述,我用“C1 代码”来指代由 C1 生成的机器码,“C2 代码”来指代由 C2 生成的机器码。五个层级分别是:

  1. 解释执行;

  2. 执行不带 profiling 的 C1 代码;;

  3. 执行仅带方法调用次数以及循环回边执行次数 profiling 的 C1 代码;

  4. 执行带所有 profiling 的 C1 代码;

  5. 执行 C2 代码;
    其中 1 层的性能比 2 层的稍微高一些,而 2 层的性能又比 3 层高出 30%。这是因为 profiling 越多,其额外的性能开销越大。

不同层次的编译路径如下:

image.png

在 5 个层次的执行状态中,1 层和 4 层为终止状态。当一个方法被终止状态编译过后,如果编译后的代码并没有失效,那么 Java 虚拟机是不会再次发出该方法的编译请求的。

九、-XX:+UseCompressedOops

当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从 32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍。这也会对 CPU 缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到 64 位的 JVM 主要动机在于可以指定最大堆大小,通过压缩 OOP 可以节省一定的内存。通过 -XX:+UseCompressedOops 选项,JVM 会使用 32 位的 OOP,而不是 64 位的 OOP。
简单理解:64位JVM中压缩内存存储32位的数据。减少内存开销。

十、-XX:SoftRefLRUPolicyMSPerMB=50

十一、-XX:MaxTenuringThreshold=10

设置在年轻代(Young Generation)中对象存活次数(经过Minor GC的次数)后仍然存活,就会晋升到老年代(Old Generation)。

十二、-XX:CICompilerCount=4

设置最大并行编译数为4。

十三、-XX:-OmitStackTraceInFastThrow

省略异常栈信息从而快速抛出。

十四、-Dkotlinx.coroutines.debug=on

版权声明

非特殊说明,本文由Zender原创或收集发布,欢迎转载。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

作者文章
热门