前面的一篇文章《字符串鏈接你用+仍是用StringBuilder》,有朋友找我反饋了一些問題,其中一位朋友說JDK10下生成的字節碼跟文章中並不同,這裏繼續看下是什麼狀況。java
以下圖,按照《字符串鏈接你用+仍是用StringBuilder》的代碼在 javap 後發現它並無建立 StringBuilder 類和一些相應的操做,與文章的描述的並不符合,使用的JDK版本爲JDK10。mysql
JDK9及之後的編譯器已經改爲用動態指令執行字節碼了,具體的調用實如今 java.lang.invoke.StringConcatFactory 類中,也就是說指令沒有生成到class文件中保存起來,而是運行時生成。 具體實現以下,有六種策略,前五種仍是用StringBuilder實現。 sql
對於下面簡單的例子,JDK8及以前和JDK9及以後編譯的字節碼有什麼差異數組
public class TestString2 {
public static void main(String[] args) {
String s = "www";
for (int i = 0; i < 10; i++)
s += i;
}
}
複製代碼
JDK8及以前,bash
public class com.seaboat.string.TestString2 {
public com.seaboat.string.TestString2();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String www
2: astore_1
3: iconst_0
4: istore_2
5: goto 30
8: new #18 // class java/lang/StringBuilder
11: dup
12: aload_1
13: invokestatic #20 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
16: invokespecial #26 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
19: iload_2
20: invokevirtual #29 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
23: invokevirtual #33 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
26: astore_1
27: iinc 2, 1
30: iload_2
31: bipush 10
33: if_icmplt 8
36: return
}
複製代碼
JDK9及以後,網絡
public class com.seaboat.string.TestString2 {
public com.seaboat.string.TestString2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String www
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 25
11: aload_1
12: iload_2
13: invokedynamic #3, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String;
18: astore_1
19: iinc 2, 1
22: goto 5
25: return
}
複製代碼
能夠看到JDK9以後生成的字節碼是比較簡潔的,只有一個 InvokeDynamic 指令,編譯器會給該類字節碼增長 invokedynamic 指令相關內容,包括方法句柄、引導方法、調用點、方法類型等等。它會調用 java.lang.invoke.StringConcatFactory 類中的makeConcatWithConstants
方法,它有六種策略來處理字符串。以下代碼所示,有默認的策略,也能夠經過java.lang.invoke.stringConcat
啓動參數來修改策略。併發
private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
static {
STRATEGY = DEFAULT_STRATEGY;
Properties props = GetPropertyAction.privilegedGetProperties();
final String strategy =
props.getProperty("java.lang.invoke.stringConcat");
CACHE_ENABLE = Boolean.parseBoolean(
props.getProperty("java.lang.invoke.stringConcat.cache"));
DEBUG = Boolean.parseBoolean(
props.getProperty("java.lang.invoke.stringConcat.debug"));
final String dumpPath =
props.getProperty("java.lang.invoke.stringConcat.dumpClasses");
STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
}
複製代碼
private enum Strategy {
BC_SB,
BC_SB_SIZED,
BC_SB_SIZED_EXACT,
MH_SB_SIZED,
MH_SB_SIZED_EXACT,
MH_INLINE_SIZED_EXACT
}
複製代碼
有六種策略,前五種仍是用StringBuilder實現,而默認的策略MH_INLINE_SIZED_EXACT
,這種策略下是直接使用字節數組來操做,而且字節數組長度預先計算好,能夠減小字符串複製操做。實現的核心是經過 MethodHandle 來實現 runtime,具體實現邏輯在MethodHandleInlineCopyStrategy.generate
方法中。app
private static MethodHandle generate(Lookup lookup, String className, MethodType mt, Recipe recipe) throws StringConcatException {
try {
switch (STRATEGY) {
case BC_SB:
return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.DEFAULT);
case BC_SB_SIZED:
return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED);
case BC_SB_SIZED_EXACT:
return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED_EXACT);
case MH_SB_SIZED:
return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED);
case MH_SB_SIZED_EXACT:
return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED_EXACT);
case MH_INLINE_SIZED_EXACT:
return MethodHandleInlineCopyStrategy.generate(mt, recipe);
default:
throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
}
} catch (Error | StringConcatException e) {
throw e;
} catch (Throwable t) {
throw new StringConcatException("Generator failed", t);
}
}
複製代碼
-------------推薦閱讀------------機器學習
個人開源項目彙總(機器&深度學習、NLP、網絡IO、AIML、mysql協議、chatbot)分佈式
跟我交流,向我提問:
公衆號的菜單已分爲「讀書總結」、「分佈式」、「機器學習」、「深度學習」、「NLP」、「Java深度」、「Java併發核心」、「JDK源碼」、「Tomcat內核」等,可能有一款適合你的胃口。
歡迎關注: