关于String的内存模型

原创 2016年08月29日 21:09:16

首先,我还是从一个题目开始吧:

有如下一段代码,请选择其运行结果()

public class StringDemo{
  private static final String MESSAGE="taobao";
  public static void main(String [] args) {
    String a ="tao"+"bao";
    String b="tao";
    String c="bao";
    System.out.println(a==MESSAGE);
    System.out.println((b+c)==MESSAGE);
  }
}

true true
false false
true false
false true
分析:

对于这道题,考察的是对String类型的认识以及编译器优化。Java中String不是基本类型,但是有些时候和基本类型差不多,如String b =  "tao" ; 可以对变量直接赋值,而不用 new 一个对象(当然也可以用 new)。所以String这个类型值得好好研究下。

Java中的变量和基本类型的值存放于栈内存,而new出来的对象本身存放于堆内存,指向对象的引用还是存放在栈内存。例如如下的代码:

int  i=1;

    String s =  new  String( "Hello World" );

变量i和s以及1存放在栈内存,而s指向的对象”Hello World”存放于堆内存。

 

 

 

 

栈内存的一个特点是数据共享,这样设计是为了减小内存消耗,前面定义了i=1,i和1都在栈内存内,如果再定义一个j=1,此时将j放入栈内存,然后查找栈内存中是否有1,如果有则j指向1。如果再给j赋值2,则在栈内存中查找是否有2,如果没有就在栈内存中放一个2,然后j指向2。也就是如果常量在栈内存中,就将变量指向该常量,如果没有就在该栈内存增加一个该常量,并将变量指向该常量。

 

 

如果j++,这时指向的变量并不会改变,而是在栈内寻找新的常量(比原来的常量大1),如果栈内存有则指向它,如果没有就在栈内存中加入此常量并将j指向它。这种基本类型之间比较大小和我们逻辑上判断大小是一致的。如定义i和j是都赋值1,则i==j结果为true。==用于判断两个变量指向的地址是否一样。i==j就是判断i指向的1和j指向的1是同一个吗?当然是了。对于直接赋值的字符串常量(如String s=“Hello World”;中的Hello World)也是存放在栈内存中,而new出来的字符串对象(即String对象)是存放在堆内存中。如果定义String s=“Hello World”和String w=“Hello World”,s==w吗?肯定是true,因为他们指向的是同一个Hello World。

 

 

堆内存没有数据共享的特点,前面定义的String s =  new  String( "Hello World" );后,变量s在栈内存内,Hello World 这个String对象在堆内存内。如果定义String w = new  String( "Hello World" );,则会在堆内存创建一个新的String对象,变量w存放在栈内存,w指向这个新的String对象。堆内存中不同对象(指同一类型的不同对象)的比较如果用==则结果肯定都是false,比如s==w?当然不等,s和w指向堆内存中不同的String对象。如果判断两个String对象相等呢?用equals方法。

 

 

 

说了这么多只是说了这道题的铺垫知识,还没进入主题,下面分析这道题。 MESSAGE 成员变量及其指向的字符串常量肯定都是在栈内存里的,变量 a 运算完也是指向一个字符串“ taobao ”啊?是不是同一个呢?这涉及到编译器优化问题。对于字符串常量的相加,在编译时直接将字符串合并,而不是等到运行时再合并。也就是说

String a =  "tao" + "bao" ;和String a =  "taobao" ;编译出的字节码是一样的。所以等到运行时,根据上面说的栈内存是数据共享原则,a和MESSAGE指向的是同一个字符串。而对于后面的(b+c)又是什么情况呢?b+c只能等到运行时才能判定是什么字符串,编译器不会优化,想想这也是有道理的,编译器怕你对b的值改变,所以编译器不会优化。运行时b+c计算出来的"taobao"和栈内存里已经有的"taobao"是一个吗?不是。b+c计算出来的"taobao"应该是放在堆内存中的String对象。这可以通过System. out .println( (b+c)==MESSAGE );的结果为false来证明这一点。如果计算出来的b+c也是在栈内存,那结果应该是true。Java对String的相加是通过StringBuffer实现的,先构造一个StringBuffer里面存放”tao”,然后调用append()方法追加”bao”,然后将值为”taobao”的StringBuffer转化成String对象。StringBuffer对象在堆内存中,那转换成的String对象理所应当的也是在堆内存中。下面改造一下这个语句System. out.println( (b+c).intern()== MESSAGE );结果是true, intern() 方法会先检查 String 池 ( 或者说成栈内存 ) 中是否存在相同的字符串常量,如果有就返回。所以 intern()返回的就是MESSAGE指向的"taobao"。再把变量b和c的定义改一下,

final  String b =  "tao" ;

         final  String c =  "bao" ;

            

       System. out .println( (b+c)== MESSAGE );

现在b和c不可能再次赋值了,所以编译器将b+c编译成了”taobao”。因此,这时的结果是true。

在字符串相加中,只要有一个是非final类型的变量,编译器就不会优化,因为这样的变量可能发生改变,所以编译器不可能将这样的变量替换成常量。例如将变量b的final去掉,结果又变成了false。这也就意味着会用到StringBuffer对象,计算的结果在堆内存中。

    如果对指向堆内存中的对象的String变量调用intern()会怎么样呢?实际上这个问题已经说过了,(b+c).intern(),b+c的结果就是在堆内存中。对于指向栈内存中字符串常量的变量调用intern()返回的还是它自己,没有多大意义。它会根据堆内存中对象的值,去查找String池中是否有相同的字符串,如果有就将变量指向这个string池中的变量。

String a = "tao"+"bao";
       String b = new String("taobao");
     
      System.out.println(a==MESSAGE); //true
      System.out.println(b==MESSAGE);  //false
     
      b = b.intern();
      System.out.println(b==MESSAGE); //true
System. out .println(a==a.intern());  //true



版权声明:本文为博主原创文章,未经博主允许不得转载。

Java String 变量的内存模型

Java中对于String 类型变量声明有两种方式: 1、 String str1 = "abc"; 2、 String str2 = new String("abc"); 这两种方式有什么不同...
  • chenwiehuang
  • chenwiehuang
  • 2016年10月31日 10:29
  • 317

聊聊高并发(四)Java对象的表示模型和运行时内存表示

在继续了解Java内存模型之前,最好先理解Java对象的内存表示。在网上搜了下Java对象内存表示,说得都不够系统和到位。之前看了《Hotspot实战》一书,对JVM如何表示对象这块说得挺好,推荐一下...
  • ITer_ZC
  • ITer_ZC
  • 2014年10月10日 11:34
  • 5381

java对象创建(内存模型)过程详解

java对象创建详细过程步骤、jvm内存结构:声明对象引用和创建对象实体。类信息、对象引用、对象实体均在内存的不同区域。 内存结构 每一个java应用程序均会唯一的对应一个jvm实例,而这个jvm实...
  • Recall_Tomorrow
  • Recall_Tomorrow
  • 2017年04月09日 14:35
  • 817

由常量池 运行时常量池 String intern方法想到的(三)之String内存模型

前面的文章由常量池 运行时常量池 String intern方法想到的(二) 说了一些关于java字节码的东西,这篇博文围绕String做一些总结。String的内存布局s = “12”public ...
  • fan2012huan
  • fan2012huan
  • 2016年03月17日 15:41
  • 1377

Java内存模型 详解

Java内存模型概念 Java平台自动集成线程以及多处理技术。内存模型描述了程序中各个变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细...
  • chenshun123
  • chenshun123
  • 2016年03月20日 10:02
  • 1234

Java虚拟机解析篇之---内存模型

今天闲来无事来,看一下Java中的内存模型和lajihuisongjizh
  • jiangwei0910410003
  • jiangwei0910410003
  • 2014年11月02日 14:18
  • 9228

什么是Java内存模型

概述:本文向您介绍Java内存模型的概念,在C或C++中, 利用不同操作平台下的内存模型来编写并发程序;Java利用了自身虚拟机的优势, 使内存模型不束缚于具体的处理器架构,真正实现了跨平台。 ...
  • guomei
  • guomei
  • 2015年04月21日 00:51
  • 1963

JVM内存模型和垃圾收集

看《深入理解Java虚拟机》,所作读书笔记。 一、JVM体系结构 二、JVM运行时数据区 堆的划分 三、对象是否可以回收 1、引用计数算法 2、可达性分析算法 GC Roots的对象包括以...
  • u012387062
  • u012387062
  • 2015年10月25日 21:44
  • 1322

全面理解Java内存模型

Java内存模型即Java Memory Model,简称JMM。JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的。如果...
  • suifeng3051
  • suifeng3051
  • 2016年09月21日 18:39
  • 54287

JVM内核—JVM内存模型

本季内容一共分为3个课时:JVM内存模型,JVM垃圾回收,JVM类加载机制。这三部分内容是JVM的核心。...
  • qish2010
  • qish2010
  • 2016年05月09日 21:25
  • 381
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:关于String的内存模型
举报原因:
原因补充:

(最多只允许输入30个字)