博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《字符串连接你用+还是用StringBuilder》续
阅读量:6204 次
发布时间:2019-06-21

本文共 5301 字,大约阅读时间需要 17 分钟。

前言

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

问题描述

如下图,按照《》的代码在 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."
":()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);        }    }复制代码

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


跟我交流,向我提问:

公众号的菜单已分为“读书总结”、“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。

欢迎关注:

转载地址:http://fqqca.baihongyu.com/

你可能感兴趣的文章
【笔记】一些linux实用函数技巧【原创】
查看>>
创业-程序员独自5大思维障碍
查看>>
【转】Javascript Base64编码与解码
查看>>
merge into ORA-30926
查看>>
解决SublimeCodeIntel回车换行误打代码
查看>>
Javascript的setTimeOut()和setInterval()的定时器用法
查看>>
NotifyMyFrontEnd 函数背后的数据缓冲区(一)
查看>>
全球首发免费的MySql for Entity Framework Core
查看>>
iOS:多线程技术GCD的使用
查看>>
Google 最新的 Fuchsia OS【科技讯息摘要】
查看>>
javascript中对变量类型的推断
查看>>
在.NET中使用SMTP发送邮件
查看>>
ZJUT 地下迷宫 (高斯求期望)
查看>>
Lambda表达式的前世今生
查看>>
PHP绘制3D图形
查看>>
Unity Camera的两种模式
查看>>
在VirtualBox里复制VDI文件[转]
查看>>
在datatable中,在指定位置插入列
查看>>
linux下安装php的swoole扩展模块(安装后php加载不出来?)
查看>>
单播、广播、组播(多播)
查看>>