JVM进阶 -- JDK命令
jpsLists the instrumented Java Virtual Machines (JVMs) on the target system.如果Java进程关闭了默认开启的UsePerfData参数(**-XX:-UsePerfData),那么jps/jstat将无法探知**到该Java进程
123$ jps1408 Jps19 LiveCoverMain
参数
备注
m
Displays the arguments passed to the main method. The output may be null for embedded JVMs.
l
Displays the full package name for the application’s main class or the full path name to the application’s JAR file.
v
Displays the arguments passed to the JVM.
jstatMonitors Java Virtual Machine (JVM) sta ...
JVM进阶 -- 浅谈注解处理器
注解与注解处理器
注解是Java 5引入,用来为类、方法、字段和参数等Java结构提供额外信息的机制
@Override仅对Java编译器有用,为Java编译器引用一条新的编译规则,编译完成后,它的使命也结束了
Java的注解机制允许开发人员自定义注解,这些自定义注解同样可以为Java编译器添加编译规则
这种功能需要由开发人员提供,并且以插件的形式接入Java编译器中,这些插件被称之为注解处理器
除了引入新的编译规则外,注解处理器还可以用于修改已有的Java源文件(不推荐)和生成新的Java源文件
123456789@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}// 元注解@Target:用来限定目标注解所能标注的Java结构// 元注解@Retention:用来限定目标注解的生命周期// SOURCE:源代码,一旦源代码被编译为字节码,注解便会被擦除// CLASS:源代码+字节码// RUNTIME:源代码+字节码+运 ...
JVM进阶 -- 浅谈循环优化
循环无关代码外提外提无关代码
循环无关代码:循环中值不变的表达式
在不改变程序语义的情况下,将循环无关代码提出循环外
那么程序将避免重复执行这些表达式,从而达到性能提升的效果
1234567int foo(int x, int y, int[] a) { int sum = 0; for (int i = 0; i < a.length; i++) { sum += x * y + a[i]; } return sum;}
123456789101112131415161718192021222324252627// 对应的字节码int foo(int, int, int[]); Code: 0: iconst_0 1: istore 4 3: iconst_0 4: istore 5 6: goto 25// 循环体开始 9: iload 4 // load sum 11: iload_1 // load x 12: ilo ...
JVM进阶 -- 浅谈字段访问优化
概念在实际中,Java程序中的对象或许本身就是逃逸的,或许因为方法内联不够彻底而被即时编译器当成是逃逸的,这两种情况都将导致即时编译器无法进行标量替换,这时,针对对象字段访问的优化显得更为重要。
1234static int bar(Foo o, int x) { o.a = x; return o.a;}
对象o是传入参数,不属于逃逸分析的范围(JVM中的逃逸分析针对的是新建对象)
该方法会将所传入的int型参数x的值存储至实例字段Foo.a中,然后再读取并返回同一字段的值
这段代码涉及两次内存访问操作:存储和读取实例字段Foo.a
代码可以手工优化成如下
1234static int bar(Foo o, int x) { o.a = x; return x;}
即时编译器也能作出类似的自动优化
字段读取优化
即时编译器会优化实例字段和静态字段的访问,以减少总的内存访问次数
即时编译器将沿着控制流,缓存各个字段存储节点将要存储的值,或者字段读取节点所得到的值
当即时编译器遇到对同一字段的读取节点时,如果缓存值还没有失效,那么将读取节 ...
JVM进阶 -- 浅谈逃逸分析
概念
在JVM即时编译语境下,逃逸分析将判断新建的对象是否逃逸
即时编译器判断对象是否逃逸的依据
对象是否被存入堆中(静态字段或者堆中对象的实例字段)
堆是线程共享的,其他线程可以获得该对象的引用
对象是否被传入未知代码
JVM即时编译是以方法为单位的
对于方法中未被内联的方法调用,即时编译器会将其当做未知代码
方法调用的调用者以及参数是逃逸的
注:方法内联可以简单理解,在即时编译过程中遇到方法调用时
将目标方法的方法体纳入到编译范围之中,并取代原方法调用的优化手段
foreach语法糖
12345public void forEach(ArrayList<Object> list, Consumer<Object> f) { for (Object obj : list) { f.accept(obj); }}
等价代码
1234567public void forEach(ArrayList<Object> list, Consumer<Object> f) { ...
JVM基础 -- 字节码
操作数栈
JVM是基于栈的计算模型
在解析过程中,每当为Java方法分配栈帧时
执行每条执行之前,JVM要求该指令的操作数已被压入操作数栈中
在执行指令时,JVM会将该指令所需要的操作数弹出,并将该指令的结果重新压入栈中
iadd
执行iadd之前,栈顶的元素为int值1和int值2
执行iadd指令会将弹出这两个int,并将求得的和int值3压入栈中
iadd只消耗栈顶的两个元素,iadd并不关心更远的元素,也不会对它们进行修改
dup + pop
dup和pop只能处理非long和非double类型的值
long类型和double类型需要占据两个栈单元,对应使用dup2和pop2
dup
dup:复制栈顶元素
dup指令常用于复制new指令生成的未经初始化的引用
123456789101112131415public void dup() { Object o = new Object();}// 对应的字节码public void dup(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, l ...
JVM进阶 -- 浅谈即时编译
概念
即时编译是用来提升应用运行效率的技术
代码会先在JVM上解释执行,之后反复执行的热点代码会被即时翻译成为机器码,直接运行在底层硬件上
分层编译
HotSpot包含多个即时编译器:C1、C2和Graal(Java 10,实验性)
在Java 7之前,需要根据程序的特性选择对应的即时编译器
对于执行时间较短或对启动性能有要求的程序,采用编译效率较快的C1,对应参数:-client
对于执行时间较长或对峰值性能有要求的程序,采用生成代码执行效率较快的C2,对应参数:-server
Java 7引入了分层编译(-XX:+TieredCompilation),综合了C1的启动性能优势和C2的峰值性能优势
分层编译将JVM的执行状态分了5个层次
0:解释执行(也会profiling)
1:执行不带profiling的C1代码
2:执行仅带方法调用次数和循环回边执行次数profiling的C1代码
3:执行带所有profiling的C1代码
4:执行C2代码
通常情况下,C2代码的执行效率比C1代码高出30%以上
对于C1代码的三种状态,按执行效率从高至低:1层 > 2层 > 3层
1层的性 ...
JVM基础 -- Java语法糖
自动装拆箱Java代码12345public int foo() { List<Integer> list = new ArrayList<>(); list.add(0); return list.get(0);}
字节码12345678910111213141516171819202122232425public int foo(); descriptor: ()I flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=1 0: new // class java/util/ArrayList 3: dup 4: invokespecial // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: iconst_0 // 自动装箱 10: ...
JVM基础 -- 浅谈synchronized
抽象算法synchronized代码块12345public void foo(Object lock) { synchronized (lock) { lock.hashCode(); }}
12345678910111213141516171819202122232425public void foo(java.lang.Object); descriptor: (Ljava/lang/Object;)V flags: ACC_PUBLIC Code: stack=2, locals=4, args_size=2 0: aload_1 1: dup 2: astore_2 3: monitorenter 4: aload_1 5: invokevirtual #2 // Method java/lang/Object.hashCode:()I 8: pop 9: aload_2 10: ...
JVM基础 -- Java内存模型
JIT的重排序Java代码1234567891011121314public class JMM { private int a = 0; private int b = 0; public void method1() { int r2 = a; // A1 b = 1; // A2 } public void method2() { int r1 = b; // B1 a = 2; // B2 }}
单线程,method1->method2,**(r1,r2)=(1,0)**
单线程,method2->method1,**(r1,r2)=(0,2)**
多线程,没有重排序,A1->B1->A2->B2,**(r1,r2)=(0,0)**
多线程,重排序,A2->B1->B2->A1,**(r1,r2)=(1,2)**
As-If-Serial
在单线程情况下, ...