发布时间:2025-12-10 19:31:51 浏览次数:3
JAVA VM(HotSpot)「终于解决」VM运行模式JVM有两种运行模式Server与ClientClientVM:为在客户端环境中减少启动时间而优化;比较适合桌面程序,它会做一些例如像快速初始化,懒加载这一类的事件来适应桌面程序的特点(C1轻量级编译器)ServerVM:为在服务器环境中最大化程序执行速度而设计;适合做服务器程序,一些针对服务器特点的事情,比如预加载,尤其在一些并发的处理上,是会做更多的优化(C2编译器)运行模式查看java-versionjavaversion”1.8.0_141
Client VM:为在客户端环境中减少启动时间而优化;比较适合桌面程序,它会做一些例如像快速初始化,懒加载这一类的事件来适应桌面程序的特点(C1轻量级编译器) Server VM:为在服务器环境中最大化程序执行速度而设计; 适合做服务器程序,一些针对服务器特点的事情,比如预加载,尤其在一些并发的处理上,是会做更多的优化(C2编译器)JVM有两种运行模式Server与Client
java version "1.8.0_141"Java(TM) SE Runtime Environment (build 1.8.0_141-b15)Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode) 是否还在为Ide开发工具频繁失效而烦恼,来吧关注以下公众号获取最新激活方式。亲测可用!
【正版授权,激活自己账号】:Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】:官方授权 正版激活 自己使用,支持Jetbrains家族下所有IDE…
JAVA_HOME \ jre1.8.0_141 \ lib \ amd64 \ jvm.cfg-server KNOWN-client IGNORE 首先确认JDK支持哪一种或两种模式,查看JAVA_HOME \ jre1.8.0_141 \ bin目录下是否存在client或server目录,分别对应着各自的JVM,64位虚拟机只支持server模式,没有client目录
检查是否有语法错误,如果没有就将其翻译成JVM可识别的字节码文件(即.class文件),把源代码(高级语言)转换成(翻译)低级语言(机器语言)的程序
前端编译器:主要是词法分析、语法分析、语义分析,然后生成一个中间表达形式(IR:Intermediate Representation)(即.class文件),例如:Sun的javac、Eclipse的JDT的增量式编译器
后端编译器:主要是把中间表达形式IR(即.class文件)进行优化,最终生成目标机器码
后端编译器在JVM中有两种:JIT(Just In Time Compiler)及时、AOT(Ahead Of Time Compiler) 提前
JVM分配内存,首先会逐条读取IR的指令来执行,这个过程就是解释执行的过程。当某一方法调用次数达到即时编译定义的阈值时,就会触发即时编译,这时即时编译器会将IR进行优化,并生成这个方法的机器码,后续再调用这个方法时,就会直接调用机器码执行,这个就是编译执行的过程
JIT编译器_百度百科
JIT编译器,英文写作Just-In-Time Compiler,中文意思是即时编译器。
JIT是一种提高程序运行效率的方法。通常,程序有两种运行方式:静态编译与动态解释。静态编译的程序在执行前全部被翻译为机器码,而动态解释执行的则是一句一句边运行边翻译。
HotSpot虚拟机中内置了两个JIT编译器:Client Complier(C1编译速度)和Server Complier(C2编译质量),分别用在客户端和服务端,Java10以后新增了一个编译器Graal
C1编译器对应参数 -client,对于执行时间较短,对启动性能有要求的程序,可以选C1
C2编译器对应参数 -server,对峰值性能有要求的程序,可以选C25
分层编译(Tiered Compiation)工作模式出现之前,HotSpot VM 默认采用解释器与其中一个编译器直接配合的方式工作
分层编译,分层编译时C1和C2有可能同时工作,使用-XX:+TieredCompilation参数开启,它综合了C1的启动性能和C2的峰值性能优势。
JDK8默认开启了分层编译
编译级别有:
0:解释执行
1:简单C1编译代码
2:受限的C1编译代码
3:完全C1编译代码
4:C2编译代码
java -versionjava version "1.8.0_141"Java(TM) SE Runtime Environment (build 1.8.0_141-b15)Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode) 解释模式 java -Xint -versionjava version "1.8.0_141"Java(TM) SE Runtime Environment (build 1.8.0_141-b15)Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, interpreted mode) 编译模式 优先采用编译,无法编译时也会解释执行(在最新的HotSpot中此参数被取消)
java -Xcomp -versionjava version "1.8.0_141"Java(TM) SE Runtime Environment (build 1.8.0_141-b15)Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, compiled mode) 测试 public class JITDemo { private static Random random = new Random(); public static void main(String[] args) { long start = System.currentTimeMillis(); int count = 0; int i = 0; while (i++ < 99999999){ count += plus(); } System.out.println("time cost : " + (System.currentTimeMillis() - start)); } private static int plus() { return random.nextInt(10); }} 解释执行模式结果:
-Xint -XX:+PrintCompilationtime cost : 44619 编译执行模式结果:
-Xcomp -XX:+PrintCompilation1080 1 n 0 sun.misc.Unsafe::objectFieldOffset (native) 1081 2 n 0 java.security.AccessController::doPrivileged (native) (static) 1087 3 !b 3 java.lang.invoke.MethodHandle::<clinit> (45 bytes) 1088 4 !b 4 java.lang.invoke.MethodHandle::<clinit> (45 bytes) 1088 5 b 3 java.lang.ClassLoader$NativeLibrary::getFromClass (13 bytes) 1089 3 ! 3 java.lang.invoke.MethodHandle::<clinit> (45 bytes) made not entrant 1089 6 b 4 java.lang.ClassLoader$NativeLibrary::getFromClass (13 bytes) 混合模式结果:
-XX:+PrintCompilation 1038 110 3 java.util.Hashtable$Entry::<init> (26 bytes) 1038 113 3 java.lang.StringBuffer::<init> (6 bytes) 1039 114 s 3 java.lang.StringBuffer::toString (36 bytes) 1419 62 % 4 com.xxx.config.JITDemo2::main @ -2 (58 bytes) made not entrant 1421 115 % 3 com.xxx.config.JITDemo2::main @ 9 (58 bytes) 1423 116 % 4 com.xxx.config.JITDemo2::main @ 9 (58 bytes) 1426 115 % 3 com.xxx.config.JITDemo2::main @ -2 (58 bytes) made not entrant 2372 116 % 4 com.xxx.config.JITDemo2::main @ -2 (58 bytes) made not entranttime cost : 1678 第1列:为JVM启动后到该方法被编译相隔的时间,单位为毫秒
第2列:编译ID,用来跟踪一个方法的编译、优化、深度优化
第3列:
b Blocking compiler (always set for client)* Generating a native wrapper% On stack replacement (where the compiled code is running)! Method has exception handlerss Method declared as synchronizedn Method declared as nativemade non entrant compilation was wrong/incomplete, no future callers will use this versionmade zombie code is not in use and ready for GC 第4列 com.xxx.config.JITDemo2::main:被编译的方法
第5列 (67 bytes):方法的字节大小
JIT编译。触发了JIT编译后,在默认设置下,执行引擎并不会同步等待编译请求完成,而是继续进入解释器按照解释方式执行字节码,直到提交的请求被编译器编译完成为止(编译工作在后台线程中进行)。当编译工作完成后,下一次调用该方法或代码时,就会使用已编译的版本。
JVM在调用一个方法时,会在计数器上+1,如果方法里面有循环体,每次循环,计数器也会+1
在不启用分层编译时,当某一方法的计数器达到由参数-XX:CompileThreshold指定的阈值时(C1为1500,C2为10000),就会触发即时编译
根据方法调用触发(不涉及循环)package com.xxx.config;import java.util.Random;// 参数:-XX:+PrintCompilation -XX:-TieredCompilation(关闭分层编译)public class JITDemo2 { private static Random random = new Random(); public static void main(String[] args) { long start = System.currentTimeMillis(); int count = 0; int i = 0; while (i++ < 15000){ System.out.println(i); count += plus(); } System.out.println("time cost : " + (System.currentTimeMillis() - start)); } // 调用时,编译器计数器+1 private static int plus() { return random.nextInt(10); }} 执行结果如下
1456214563 2020 78 % com.xxx.config.JITDemo2::main @ 9 (67 bytes)14564...15000time cost : 518 根据循环回边由于解释执行时的计数工作并没有严格与编译器同步,所以并不会是严格的10000,其实只要调用次数足够大,就可以视为热点代码,没必要做到严格同步。
package com.xxx.config;import java.util.Random;// 参数:-XX:+PrintCompilation -XX:-TieredCompilation(关闭分层编译)public class JITDemo2 { private static Random random = new Random(); public static void main(String[] args) { long start = System.currentTimeMillis(); plus(); System.out.println("time cost : " + (System.currentTimeMillis() - start)); } // 调用时,编译器计数器+1 private static int plus() { int count = 0; // 每次循环,编译器计数器+1 for (int i = 0; i < 15000; i++) { System.out.println(i); count += random.nextInt(10); } return random.nextInt(10); }} 执行结果如下
14562 1655 81 % com.xxx.config.JITDemo2::plus @ 4 (44 bytes)14563...14999time cost : 941 根据方法调用和循环回边 每次方法调用中有10次循环,所以每次方法调用计数器应该+11,所以应该会在差不多大于10000/11=909次调用时触发即时编译
package com.xxx.config;import java.util.Random;// 参数:-XX:+PrintCompilation -XX:-TieredCompilation(关闭分层编译)public class JITDemo2 { private static Random random = new Random(); public static void main(String[] args) { long start = System.currentTimeMillis(); int count = 0; int i = 0; while (i++ < 15000) { System.out.println(i); count += plus(); } System.out.println("time cost : " + (System.currentTimeMillis() - start)); } // 调用时,编译器计数器+1 private static int plus() { int count = 0; // 每次循环,编译器计数器+1 for (int i = 0; i < 10; i++) { count += random.nextInt(10); } return random.nextInt(10); }} 执行结果如下
918 1020 17 com.xxx.config.JITDemo2::plus (36 bytes)919920921 1020 922 18 java.util.Random::nextInt (74 bytes)923...9999 5315 80 ! java.io.PrintStream::println (24 bytes) 5315 81 java.io.PrintStream::print (9 bytes)...15000time cost : 738 CodeCache是热点代码的暂存区,经过即时编译器编译的代码会放在这里,它存在于堆外内存。
JDK8中server模式默认采用分层编译方式,如果需要关闭分层编译,需要加上启动参数-XX:-TieredCompilation 参数设置 初始内存大小,默认2496K-XX:InitialCodeCacheSize预留内存大小,默认48M-XX:ReservedCodeCacheSize 打印出所有参数的默认值 -XX:+PrintFlagsFinal