黑马程序员技术交流社区

标题: 【上海校区】《字符串连接你用+还是用StringBuilder》续 [打印本页]

作者: 不二晨    时间: 2018-7-19 09:36
标题: 【上海校区】《字符串连接你用+还是用StringBuilder》续
问题描述如下图,按照《字符串连接你用+还是用StringBuilder》的代码在 javap 后发现它并没有创建 StringBuilder 类和一些相应的操作,与文章的描述的并不符合,使用的JDK版本为JDK10。




问题原因JDK9及以后的编译器已经改成用动态指令执行字节码了,具体的调用实现在 java.lang.invoke.StringConcatFactory 类中,也就是说指令没有生成到class文件中保存起来,而是运行时生成。具体实现如下,有六种策略,前五种还是用StringBuilder实现。

JDK9前后对于下面简单的例子,JDK8及之前和JDK9及之后编译的字节码有什么差别
public class TestString2 {        public static void main(String[] args) {                String s = "www";                for (int i = 0; i < 10; i++)                        s += i;        }}复制代码JDK8及之前,
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}复制代码InvokeDynamic可以看到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方法中。
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);        }    }

作者:超人汪小建
链接:https://juejin.im/post/5b4be51e51882519a62f5835




作者: 不二晨    时间: 2018-7-19 10:57
优秀,奈斯
作者: 摩西摩西OvO    时间: 2018-7-19 14:26

作者: 吴琼老师    时间: 2018-7-19 16:38

作者: 摩西摩西OvO    时间: 2018-7-23 13:36

作者: 摩西摩西OvO    时间: 2018-7-26 09:18





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2