知识大全 通过JVM原理理解字符串的比较

Posted 常量

篇首语:关山初度尘未洗,策马扬鞭再奋蹄!本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 通过JVM原理理解字符串的比较相关的知识,希望对你有一定的参考价值。

通过JVM原理理解字符串的比较  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  Java中的字符串也是一连串的字符 但是与许多其他的计算机语言将字符串作为字符数组处理不同 Java将字符串作为String类型对象来处理 将字符串作为内置的对象处理允许Java提供十分丰富的功能特性以方便处理字符串

  JVM运行时数据区的内存模型由五部分组成

  ( )方法区

  ( )堆

  ( )JAVA栈

  ( )PC寄存器

  ( )本地方法栈

  对于String s = haha 它的虚拟机指令

   : ldc ; //String haha : astore_ : return

  ldc指令格式

  ldc index

  ldc指令过程 要执行ldc指令 JVM首先查找index所指定的常量池入口 在index指向的JVM常量池入口 JVM将会查找CONSTANT_Integer_info CONSTANT_Float_info和CONSTANT_String_info入口 如果还没有这些入口 JVM会解析它们 而对于上面的hahaJVM会找到CONSTANT_String_info入口 同时 将把指向被拘留String对象(由解析该入口的进程产生)的引用压入操作数栈

  astore_ 指令格式

  astore_

  astore_ 指令过程 要执行astore_ 指令 JVM从操作数栈顶部弹出一个引用类型或者returnAddress类型值 然后将该值存入由索引 指定的局部变量中 即将引用类型或者returnAddress类型值存入局部变量

  return 指令的过程

  从上面的ldc指令的执行过程可以得出 s的值是来自被拘留String对象(由解析该入口的进程产生)的引用 即可以理解为是从被拘留String对象的引用复制而来的 故我个人的理解是s的值是存在栈当中 上面是对于s值得分析 接着是对于 haha 值的分析 我们知道 对于String s = haha 其中 haha 值在JAVA程序编译期就确定下来了的 简单一点说 就是haha的值在程序编译成class文件后 就在class文件中生成了(大家可以用UE编辑器或其它文本编辑工具在打开class文件后的字节码文件中看到这个haha值) 执行JAVA程序的过程中 第一步是class文件生成 然后被JVM装载到内存执行 那么JVM装载这个class到内存中 其中的haha这个值 在内存中是怎么为其开辟空间并存储在哪个区域中呢?

  JVM常量池

  虚拟机必须为每个被装载的类型维护一个常量池 常量池就是该类型所用到常量的一个有序集和 包括直接常量(string integer和floating point常量)和对其他类型 字段和方法的符号引用 对于String常量 它的值是在常量池中的 而JVM常量池在内存当中是以表的形式存在的 对于String类型 有一张固定长度的CONSTANT_String_info表用来存储文字字符串值 注意 该表只存储文字字符串值 不存储符号引用 说到这里 对JVM常量池中的字符串值的存储位置应该有一个比较明了的理解了

  在介绍完JVM常量池的概念后 接着谈开始提到的 haha 的值的内存分布的位置 对于haha的值 实际上是在class文件被JVM装载到内存当中并被引擎在解析ldc指令并执行ldc指令之前 JVM就已经为haha这个字符串在常量池的CONSTANT_String_info表中分配了空间来存储haha这个值

  既然haha这个字符串常量存储在常量池中 常量池是属于类型信息的一部分 类型信息也就是每一个被转载的类型 这个类型反映到JVM内存模型中是对应存在于JVM内存模型的方法区中 也就是这个类型信息中的JVM常量池概念是存在于在方法区中 而方法区是在JVM内存模型中的堆中由JVM来分配的 所以 haha的值是应该是存在堆空间中的 而对于String s = new String( haha ) 它的JVM指令

   :   new              ; //class String    :   dup    :   ldc              ; //String haha    :   invokespecial    ; //Method java/lang/String :(Ljava/lang/String;)V    :   astore_     :  return 

  new指令格式 new indexbyte indexbyte

  new指令过程

  要执行new指令 Jvm通过计算(indextype << )|indextype 生成一个指向常量池的无符号 位索引 然后JVM根据计算出的索引查找JVM常量池入口 该索引所指向的常量池入口必须为CONSTANT_Class_info 如果该入口尚不存在 那么JVM将解析这个常量池入口 该入口类型必须是类 JVM从堆中为新对象映像分配足够大的空间 并将对象的实例变量设为默认值 最后JVM将指向新对象的引用objectref压入操作数栈

  dup指令格式 dup

  dup指令过程

  要执行dup指令 JVM复制了操作数栈顶部一个字长的内容 然后再将复制内容压入栈 本指令能够从操作数栈顶部复制任何单位字长的值 但绝对不要使用它来复制操作数栈顶部任何两个字长(long型或double型)中的一个字长 上面例中 即复制引用objectref 这时在操作数栈存在 个引用

  ldc指令格式 ldc index

  ldc指令过程

  要执行ldc指令 JVM首先查找index所指定的常量池入口 在index指向的JVM常量池入口 JVM将会查找CONSTANT_Integer_info CONSTANT_Float_info和CONSTANT_String_info入口 如果还没有这些入口 JVM会解析它们 而对于上面的haha JVM会找到CONSTANT_String_info入口 同时 将把指向被拘留String对象(由解析该入口的进程产生)的引用压入操作数栈

  invokespecial指令格式 invokespecial indextype indextype

  invokespecial指令过程 对于该类而言 该指令是用来进行实例初始化方法的调用 上面例子中 即通过其中一个引用调用String类的构造器 初始化对象实例 让另一个相同的引用指向这个被初始化的对象实例 然后前一个引用弹出操作数栈

  astore_ 指令格式 astore_

  astore_ 指令过程

  要执行astore_ 指令 JVM从操作数栈顶部弹出一个引用类型或者returnAddress类型值 然后将该值存入由索引 指定的局部变量中 即将引用类型或者returnAddress类型值存入局部变量

  return 指令的过程:

  从方法中返回 返回值为void 要执行astore_ 指令 JVM从操作数栈顶部弹出一个引用类型或者returnAddress类型值 然后将该值存入由索引 指定的局部变量中 即将引用类型或者returnAddress类型值存入局部变量

  通过上面 个指令 可以看出 String s = new String( haha );中的haha存储在堆空间中 而s则是在操作数栈中 上面是对s和haha值的内存情况的分析和理解;那对于String s = new String( haha );语句 到底创建了几个对象呢?这里 haha 本身就是JVM常量池中的一个对象 而在运行时执行new String()时 将JVM常量池中的对象复制一份放到堆中 并且把堆中的这个对象的引用交给s持有 所以这条语句就创建了 个String对象 下面是一些String相关的常见问题

  String中的final用法和理解

  final StringBuffer a = new StringBuffer( ); final StringBuffer b = new StringBuffer( ); a=b;//此句编译不通过 final StringBuffer a = new StringBuffer( ); a append( );//编译通过

  可见 final只对引用的 值 (即内存地址)有效 它迫使引用只能指向初始指向的那个对象 改变它的指向会导致编译期错误 至于它所指向的对象的变化 final是不负责的

  String 常量池问题的几个例子

  下面是几个常见例子的比较分析和理解

   final StringBuffer a = new StringBuffer( );    final StringBuffer b = new StringBuffer( );    a=b;//此句编译不通过    final StringBuffer a = new StringBuffer( );    a append( );//编译通过 

  分析 JVM对于字符串常量的 + 号连接 将程序编译期 JVM就将常量字符串的 + 连接优化为连接后的值 拿 a + 来说 经编译器优化后在class中就已经是a 在编译期其字符串常量的值就确定下来 故上面程序最终的结果都为true

   String a =  ab ; String bb =  b ; String b =  a  + bb;  System out println((a == b));  //result = false

  分析 JVM对于字符串引用 由于在字符串的 + 连接中 有字符串引用存在 而引用的值在程序编译期是无法确定的 即 a + bb无法被编译器优化 只有在程序运行期来动态分配并将连接后的新地址赋给b 所以上面程序的结果也就为false

   String a =  ab ;  final String bb =  b ;  String b =  a  + bb;  System out println((a == b));  //result = true

  分析 和[ ]中唯一不同的是bb字符串加了final修饰 对于final修饰的变量 它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中 所以此时的 a + bb和 a + b 效果是一样的 故上面程序的结果为true

   String a =  ab ; final String bb = getBB();  String b =  a  + bb;  System out println((a == b));  //result = false private static String getBB()  return  b ; 

  分析 JVM对于字符串引用bb 它的值在编译期无法确定 只有在程序运行期调用方法后 将方法的返回值和 a 来动态连接并分配地址为b 故上面程序的结果为false 通过上面 个例子可以得出得知

   String s =  a  +  b  +  c ;  就等价于String s =  abc ;  String a =  a ; String b =  b ;  String c =  c ;  String s = a + b + c;

  这个就不一样了 最终结果等于

   StringBuffer temp = new StringBuffer();  temp append(a) append(b) append(c);  String s = temp toString();

  由上面的分析结果 可就不难推断出String 采用连接运算符(+)效率低下原因分析 形如这样的代码

   public class Test  public static void main(String args[])   String s = null;  for(int i =  ; i <  ; i++)  s +=  a ;   

  每做一次 + 就产生个StringBuilder对象 然后append后就扔掉 下次循环再到达时重新产生个StringBuilder对象 然后 append 字符串 如此循环直至结束 如果我们直接采用 StringBuilder 对象进行 append 的话 我们可以节省 N 次创建和销毁对象的时间 所以对于在循环中要进行字符串连接的应用 一般都是用StringBuffer或StringBulider对象来进行append操作 String对象的intern方法理解和分析

   public class Test      private static String a =  ab ;     public static void main(String[] args)    String s  =  a ;    String s  =  b ;    String s = s  + s ;    System out println(s == a);//false    System out println(s intern() == a);//true           

cha138/Article/program/Java/hx/201311/25562

相关参考

知识大全 详解JVM工作原理和特点

详解JVM工作原理和特点  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  JVM工作原理和特点主要

知识大全 加深C# 中字符串前加@符号理解以及使用

加深C#中字符串前加@符号理解以及使用  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!先看代码(以下

知识大全 关于流、输入和字符的理解

     流是什么?《Beginningjava》上说是输入设备——数据来源或者输出设备——数据目标的一种抽象表示我理解的就是数据(一种你的程序要处理的数据)它可以是来自硬盘上的文件你手下键盘的输入你

知识大全 JVM,内存回收及其他

深入探索Java工作原理:JVM,内存回收及其他  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  

知识大全 classpath的理解

  jvm的类加载分三中方式  系统级别rtjar  扩展级别java_home/jre/lib/ext/目录下的jar文件  应用级别环境变量中的classpath或javac  

通俗理解鱼菜共生平衡原理

可以通俗地理解鱼菜共生的技术原理:在基本封闭的环境里,从饵料—鱼—排泄物—氨—硝化细菌—硝酸盐—植物的转化过程。在具体的实践操作中,至关重要的是要理解水培技术是一个完整的生态系统,包括鱼、植物和微生物

通俗理解鱼菜共生平衡原理

可以通俗地理解鱼菜共生的技术原理:在基本封闭的环境里,从饵料—鱼—排泄物—氨—硝化细菌—硝酸盐—植物的转化过程。在具体的实践操作中,至关重要的是要理解水培技术是一个完整的生态系统,包括鱼、植物和微生物

如何理解“夫脉者,血之府也”?

“夫脉者,血之府也”,扼要地概括了通过切脉诊病的原理。经脉是血与气汇聚之处,脉象的变化可以反映气血的盛衰变化。正如李中梓所说:“营行脉中,故为血府。然行是血者,是气为之司也。《逆顺》篇曰:‘脉之盛衰者

知识大全 采集网页图片代码

   采集网页上图片的主要关键是在怎么解析出页面代码里那些img标签的src属性在网上找了下大多都是通过字符串操作找出img标签这种方式操作起来比较麻烦而且代码看起来比较

知识大全 从实例看struts2运行原理

  简单例子  先做一个最简单的struts的例子在浏览器中请求一个action然后返回一个字符串到jsp页面上显示出来  第一步把struts最低配置的jar包加入的项目中  monslogging