当前位置: 首页 > 图灵资讯 > 技术篇> 【图灵干货】Java高级教程第十六节:JVM-字符串底层实现原理

【图灵干货】Java高级教程第十六节:JVM-字符串底层实现原理

来源:图灵教育
时间:2021-11-25 11:56:03

<a href=https://www.tulingxueyuan.cn/tlzx/jsp/627.html target=_blank class=infotextkey>JVM</a>-字符串底层实现原理

  什么样的字符串将被输入到常量池中。

  1.直接写字数。

  2.字面量的拼接结果(注意:如果字符串拼接中有一个变量,那么结果将不进入字符串常量池)

  3.调用string的intern方法可以将字符串存入一个字符串常量池。

  文字拼贴的原则。

  下面是一个列表代码。

  packagecom.hgy;

  importjava.util.Arrays;

  importjava.util.列表;

  publicclasshello{

  publicstaticvoidmain(String[]args){

  字符串="hello"+"world";

  }

  }

  查看idea中的编译类文件。

  //

  //Sourcecoderecreatedfroma.classfilebyIntelliJIDEA。

  //(poweredbyFernflowerdecompiler)

  //

  packagecom.hgy;

  publicclasshello{

  publichello(){

  }

  publicstaticvoidmain(String[]args){

  Stringa="helloworld";

  }

  }

  结论:

  通过以上两个文件,我们可以看到,在编译过程中对字符串进行了优化,直接将其合并到一个字符串中;该字符串被存储在字符串常量池中。

  线与变数拼接原则。

  java源代码。

  packagecom.hgy;

  importjava.util.Arrays;

  importjava.util.列表;

  publicclasshello{

  publicstaticvoidmain(String[]args){

  Stringv="java";

  Stringa=v+"hello"+"world";

  }

  }

  使用jclasslib查看main方法的字节码命令。

  若名词不理解请阅读请自己理解java虚拟机栈的知识。

  只需两行,就能生成如此多的字节码命令;我在代码中简单地解释了它们各自的功能,

  0ldc#2//将java从String常量池中装载。

  2astore_1//将常数存储在一个索引为1的本地变量表中。

  3new#3//为StringBuilder对象分配内存空间。

  6dup

  7invokespecial#4>//对StringBuilder执行构型。

  造方法

  10aload_1//获得一个本地变量表索引为1的引用地址,

  11invokevirtual#5//将上面装载的内容作为参数。

  传入append方法。

  14ldc#6//将helloworld从string常量池中装载。

  16invokevirtual#5//将上面装载的内容作为参数。

  传入append方法。

  19invokevirtual#7//调用toString方法。

  22astore_2//将结果存储在一个局部变量表中。

  23雷顿。

  我们可以知道字符串拼接实际上就是创建一个StringBuilder对象,然后将append内容包含在内,最后调用toString方法以获取结果。

  3.1为何结果未存储在常量池。

  根据上面的字节码指令,字符串拼接结果是StringBuilder的toString方法的结果,那么toString中具体有什么作用,又是为什么结果不在常量池中?

  这里有一个StringBuilder.toString和字节码指令。

  @Override

  发布StringtoString(){

  //Createacopy,don'tsharethearray

  //此处value使用char数组【JDK8版】

  returnnewString(value,0,count);

  }

  0new#80。

  3dup

  4aload_0。

  5getfield#234。

  8iconst_0。

  9aload_0

  10getfield#233。

  13invokespecial#291>。

  16areturn。

  我们可以很好地解释到,事实上,我们最终调用了String的构造方法,传递一个char数组,这样,最终的结果肯定也会出现在我们身边。

  为何串接效率不高。

  4.1准备源代码。

  先写两种方法,一种是使用字符串拼接,一种是StringBuilder;

  publicclasshello{

  publicvoidconcatStrByDefault(){

  字符串="name";

  {for(inti=0;i<100;i++)

  basic+=i;

  }

  system.out.print(basic);

  }

  publicvoidconcatStrByBuilder{

  StringBuilderbasic=newStringBuilder("name");

  {for(inti=0;i<100;i++)

  basic.append(i);

  }

  system.out.println(basic.toString);

  }

  }

  4.2字节码的指令层次的分析。

  一上代码执行时间一长我就不再重复测试了相信大家会这样,下一步我们一起来看看这两种方法字节码指令。

  下面是concatStrByDefault方法的字节码指令。

  简述如下:循环是将33行的goto指令调至第5行,因此连续循环;11行即循环中不停地通过new创建StringBuilder对象,即创建了多少次StringBuilder对象。

  物体,如果看过我之前写字符串拼接的原理,那么StringBuilder的toString方法中也new了一个String对象;在这里,所有这些对象的创建必然要求垃圾回收效率低。

  0ldc#2。

  2astore_1。

  3iconst_0。

  4istore_2。

  5iload_2。

  6bipush100。

  8if_icmpge36(+28)

  11new#3。

       图灵学院成立于2017年7月15日,现阶段提供 计算机基础原理、JavaSE核心、Java后端、 面试必备算法、python核心编程、数据分析、web 开发题、人工智能等专题课程,为想学习Python的学员提供优质的培训服务,帮助学员掌握更加全面的技能,是计算机人员职场中提职加薪的首选。
       免费java架构师视频学习地址:免费视频