JIT的比较冷门,首先你要读一下这两篇 帖子:
《面向JIT编程-方法内联》 https://blog.csdn.net/u012834750/article/details/79488572
《浅谈对JIT编译器的理解》 https://www.cnblogs.com/insistence/p/5901457.html
后面就是实战的内容了:
一、我们先看方法内联:
根据帖子里面说的,方法内联只会发生在private方法中,那我们来看一下是不是这样。
1 public static void main(String[] args) throws ClassNotFoundException { 2 for (int i = 0; i < 1000000; i++) { 3 name(i); 4 } 5 } 6 7 public static void name(int k) { 8 for (int i = 0; i < k; i++) { 9 name1(i); 10 } 11 } 12 13 private static void name1(int i) { 14 i = i * 100; 15 } 16}
用javac命令编译后,用javap命令查看汇编情况,发现私有函数name1()被内联,我们知道内联最大的好处就是去掉了压栈 弹栈,提升性能。
1D:\software\eclipse\eclipse-workspace_solo\TESTTT\src>javap -c Main2 2Compiled from "Main2.java" 3public class Main2 { 4 public Main2(); 5 Code: 6 0: aload_0 7 1: invokespecial #1 // Method java/lang/Object."<init>":()V 8 4: return 9 10 public static void main(java.lang.String[]) throws java.lang.ClassNotFoundException; 11 Code: 12 0: iconst_0 13 1: istore_1 14 2: iload_1 15 3: ldc #2 // int 1000000 16 5: if_icmpge 18 17 8: iload_1 18 9: invokestatic #3 // Method name:(I)V 19 12: iinc 1, 1 20 15: goto 2 21 18: return 22 23 public static void name(int); 24 Code: 25 0: iconst_0 26 1: istore_1 27 2: iload_1 28 3: iload_0 29 4: if_icmpge 17 30 7: iload_1 31 8: invokestatic #4 // Method name1:(I)V 32 11: iinc 1, 1 33 14: goto 2 34 17: return 35}
二、下面我们来看一下JIT编译:
先看下面代码:
1public class Main2 { 2 public static void main(String[] args) throws ClassNotFoundException { 3 for (int i = 0; i < 1000000; i++) { 4 name(i); 5 } 6 } 7 8 public static void name(int k) { 9 for (int i = 0; i < k; i++) { 10 name1(i); 11 } 12 } 13 14 private static void name1(int i) { 15 i = i * 100; 16 } 17}
编译后,我们进行运行,但在运行的时候,加上:-XX:+PrintCompilation 参数,这样会把JIT编译的参数打印出来。
1D:\software\eclipse\eclipse-workspace_solo\TESTTT\src>java -XX:+PrintCompilation Main2 2 71 1 3 java.lang.String::hashCode (55 bytes) 3 72 2 3 java.lang.String::equals (81 bytes) 4 73 3 4 java.lang.String::charAt (29 bytes) 5 73 4 3 java.lang.String::<init> (82 bytes) 6 74 10 4 java.lang.String::length (6 bytes) 7 74 6 3 java.lang.Character::toLowerCase (9 bytes) 8 74 11 n 0 java.lang.System::arraycopy (native) (static) 9 75 7 3 java.lang.CharacterData::of (120 bytes) 10 75 8 3 java.lang.CharacterDataLatin1::toLowerCase (39 bytes) 11 76 9 3 java.lang.CharacterDataLatin1::getProperties (11 bytes) 12 76 5 3 java.util.Arrays::copyOfRange (63 bytes) 13 80 12 3 java.lang.Object::<init> (1 bytes) 14 81 13 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes) 15 82 15 3 java.lang.String::indexOf (70 bytes) 16 82 17 1 java.lang.Object::<init> (1 bytes) 17 82 12 3 java.lang.Object::<init> (1 bytes) made not entrant 18 83 14 3 java.lang.AbstractStringBuilder::append (29 bytes) 19 84 18 3 java.io.WinNTFileSystem::isSlash (18 bytes) 20 84 19 s 3 java.lang.StringBuffer::append (13 bytes) 21 85 16 3 java.lang.StringBuilder::append (8 bytes) 22 85 20 3 java.lang.AbstractStringBuilder::append (50 bytes) 23 88 21 3 Main2::name1 (6 bytes) 24 88 22 3 Main2::name (18 bytes) 25 88 23 1 Main2::name1 (6 bytes) 26 89 24 % 4 Main2::name @ 2 (18 bytes) 27 90 21 3 Main2::name1 (6 bytes) made not entrant 28 91 25 4 Main2::name (18 bytes) 29 91 22 3 Main2::name (18 bytes) made not entrant 30 92 26 % 3 Main2::main @ 2 (19 bytes) 31 93 27 3 Main2::main (19 bytes) 32 93 28 % 4 Main2::main @ 2 (19 bytes) 33 94 26 % 3 Main2::main @ -2 (19 bytes) made not entrant 34 95 28 % 4 Main2::main @ -2 (19 bytes) made not entrant
通过观察,我们发现name和name1函数都被进行JIT编译了,因为他们都是高热点函数,并且执行次数较多。
那么我们来看一下,如果执行次数比较少的时候,是什么情况呢?我们修改一下代码:
1public class Main2 { 2 public static void main(String[] args) throws ClassNotFoundException { 3 for (int i = 0; i < 100; i++) { // 这个地方1000000 变成了100 4 name(i); 5 } 6 } 7 8 public static void name(int k) { 9 for (int i = 0; i < k; i++) { 10 name1(i); 11 } 12 } 13 14 private static void name1(int i) { 15 i = i * 100; 16 } 17}
修改后,我们再执行,在看JIT编译函数,你会发现name和name1都不见了,这也验证了JIT是运行时,C1或者C2编译器发现热点函数,进行重新编译起到作用。
1D:\software\eclipse\eclipse-workspace_solo\TESTTT\src>java -XX:+PrintCompilation Main2 2 73 1 3 java.lang.String::hashCode (55 bytes) 3 74 2 3 java.lang.String::equals (81 bytes) 4 75 3 4 java.lang.String::charAt (29 bytes) 5 75 4 3 java.lang.String::<init> (82 bytes) 6 75 11 n 0 java.lang.System::arraycopy (native) (static) 7 77 10 4 java.lang.String::length (6 bytes) 8 77 6 3 java.lang.Character::toLowerCase (9 bytes) 9 78 7 3 java.lang.CharacterData::of (120 bytes) 10 78 8 3 java.lang.CharacterDataLatin1::toLowerCase (39 bytes) 11 79 9 3 java.lang.CharacterDataLatin1::getProperties (11 bytes) 12 79 5 3 java.util.Arrays::copyOfRange (63 bytes) 13 83 12 3 java.lang.Object::<init> (1 bytes) 14 83 13 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes) 15 84 15 3 java.lang.String::indexOf (70 bytes) 16 85 18 1 java.lang.Object::<init> (1 bytes) 17 85 12 3 java.lang.Object::<init> (1 bytes) made not entrant 18 86 14 3 java.lang.AbstractStringBuilder::append (29 bytes) 19 87 17 3 java.lang.StringBuilder::append (8 bytes) 20 88 16 3 java.io.WinNTFileSystem::isSlash (18 bytes) 21 88 19 s 3 java.lang.StringBuffer::append (13 bytes) 22 93 20 3 Main2::name1 (6 bytes)