java的字符链接用什么_《字符串连接你用+还是用StringBuilder》续

前言

前面的一篇文章《字符串连接你用+还是用StringBuilder》,有朋友找我反馈了一些问题,其中一位朋友说JDK10下生成的字节码跟文章中并不一样,这里继续看下是什么情况。

问题描述

如下图,按照《字符串连接你用+还是用StringBuilder》的代码在 javap 后发现它并没有创建 StringBuilder 类和一些相应的操作,与文章的描述的并不符合,使用的JDK版本为JDK10。

8a5fb98d7c08aa995d987cc5ba4d917f.png

2a6ff51e0f1303d13189b1ce2e9d9971.png

问题原因

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

具体实现如下,有六种策略,前五种还是用StringBuilder实现。

2019070531_3.png

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."":()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."":(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."":()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);

}

}

————-推荐阅读————

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值