文档章节

关于String内存分配的深入探讨

 我类个擦
发布于 2013/05/28 18:16
字数 1587
阅读 4250
收藏 25
点赞 5
评论 3

public class Test {

   

    public 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); 

    }

}

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

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

int i=1;

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

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

 

 

 

 

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

 

 

如果j++,这时指向的变量并不会改变,而是在栈内寻找新的常量(比原来的常量大1),如果栈内存有则指向它,如果没有就在栈内存中加入此常量并将j指向它。这种基本类型之间比较大小和我们逻辑上判断大小是一致的。如定义ij是都赋值1,则i==j结果为true==用于判断两个变量指向的地址是否一样。i==j就是判断i指向的1j指向的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?当然不等,sw指向堆内存中不同的String对象。如果判断两个String对象相等呢?用equals方法。

 

 

 

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

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

final String b = "tao";

        final String c = "bao";

            

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

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

在字符串相加中,只要有一个是非final类型的变量,编译器就不会优化,因为这样的变量可能发生改变,所以编译器不可能将这样的变量替换成常量。例如将变量bfinal去掉,结果又变成了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

© 著作权归作者所有

共有 人打赏支持
粉丝 10
博文 31
码字总数 31164
作品 0
杭州
程序员
加载中

评论(3)

BennyTian
BennyTian
写的真好~
zzuqiang
zzuqiang
java对String的确很复杂!
夲仒無道
夲仒無道
涨知识了。
文本在内存中的编码(1)——乱码探源(4)

让我们从一个故事开始说起。话说北大是很有哲学传统的,当你准备踏进北大校门时,连门卫都会连问你三个终极哲学问题: 你是谁?你从哪里来?你要到哪里去? 那么这与我们的问题又有何关系呢?...

国栋 ⋅ 2015/06/26 ⋅ 0

深入理解C语言结构体成员变量内存分配

欢迎点击「算法与编程之美」↑关注我们! 本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列博客。 1 问题描述 在学习C语言的时候,我们都会频繁的接触到结构体,使用结...

算法与编程之美 ⋅ 04/05 ⋅ 0

java内存分配和String类型的深度解析

转自:http://my.oschina.net/xiaohui249/blog/170013 摘要: 从整体上介绍java内存的概念、构成以及分配机制,在此基础上深度解析java中的String类型,从内存分配情况来解析String对象的特性...

Turnsole1 ⋅ 01/03 ⋅ 0

java内存分配和String类型的深度解析

一、引题 在java语言的所有数据类型中,String类型是比较特殊的一种类型,同时也是面试的时候经常被问到的一个知识点,本文结合java内存分配深度分析关于String的许多令人迷惑的问题。下面是...

萧十一郎君 ⋅ 2013/10/19 ⋅ 30

Go 中 slice 的那些事

Go 一、定义 我们都知道在 Go 语言中,数组的长度是不可变的,那么为了更加灵活的处理数据,Go 提供了一种功能强悍的类型切片(slice),slice 可以理解为 “动态数组”。但是 slice 并不是真...

HenryCheng ⋅ 2017/11/08 ⋅ 0

Android深入浅出

Handler 源码注释翻译 让你无缝了解 Handler~ Android:这是一份很详细的 Socket 使用攻略 所有关于 Android Socket 的使用都在这里了 由Message,Handler,MessageQueue和Looper引发的思考?...

掘金官方 ⋅ 01/04 ⋅ 0

Java内存模型小析值JVM运行时数据区域(一)

之前看过一次周志明写的《深入理解Java虚拟机-JVM高级特性与最佳实践》但是看过之后很多东西就忘了如同失忆了一般,所以这次在看的时候做一个读书笔记,以后也便于复习。先奉上一副自己总结的...

木叶之荣 ⋅ 2017/05/07 ⋅ 0

关于c语言结构体成员变量访问方式的一点思考

前言 上篇博文(关于c语言结构体偏移的一点思考)对c语言中结构体偏移做了一些思考,发现博文中还有一些小的问题,没有描述的足够清楚,所以才萌生了本篇博文的想法。 为什么不直接将本篇博文作...

算法与编程之美 ⋅ 2013/06/27 ⋅ 11

深入Java核心 Java内存分配原理精讲

JAVA内存分配 与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入Java核心,详细介 绍一下Java在内存分配方面的知识。一般...

红薯 ⋅ 2010/09/12 ⋅ 4

关于Cstring 总结

前言:串操作是编程中最常用也最基本的操作之一. 做为VC程序员,无论是菜鸟或高手都曾用过CString.而且好像实际编程中很难离得开它(虽然它不是标准C++中的库).因为MFC中提供的这个类对 我们...

buleberry ⋅ 2014/03/14 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Mahout推荐算法API详解

前言 用Mahout来构建推荐系统,是一件既简单又困难的事情。简单是因为Mahout完整地封装了“协同过滤”算法,并实现了并行化,提供非常简单的API接口;困难是因为我们不了解算法细节,很难去根...

xiaomin0322 ⋅ 21分钟前 ⋅ 0

WampServer默认web服务器根目录位置

安装WampServer之后的web服务器根目录默认位置在WampServer安装目录下的www:

临江仙卜算子 ⋅ 23分钟前 ⋅ 0

Redux的一些手法记录

Redux Redux的基本概念见另一篇文。 这里记录一下Redux在项目中的实际操作的手法。 actions 首先定义action.js,actions的type,可以另起一个action-type.js文件。 action-type.js用来存...

LinearLaw ⋅ 24分钟前 ⋅ 0

android 手势检测(左右滑动、上下滑动)

GestureDetector类可以让我们快速的处理手势事件,如点击,滑动等。 使用GestureDetector分三步: 1. 定义GestureDetector类 2. 初始化手势类,同时设置手势监听 3. 将touch事件交给gesture...

王先森oO ⋅ 38分钟前 ⋅ 0

java 方法的执行时间监控 设置超时(Future 接口)

java 方法的执行时间监控 设置超时(Future 接口) import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor......

青峰Jun19er ⋅ 43分钟前 ⋅ 0

一名开源小白的Apache成长自述

今天收到了来自Apache Vote我成为Serviceomb项目Committer的邮件,代表自己的贡献得到了充分的肯定;除了感谢团队的给力支持,我更希望将自己的成长经历——如何践行Apache Way的心得介绍给大...

微服务框架 ⋅ 45分钟前 ⋅ 0

vim介绍、颜色显示和移动光标、一般模式下复制、剪切和粘贴

1.vim 是 vi 的升级版 vim 是带有颜色显示的 mini安装的系统,一般都不带有vim [root@aminglinux-128 ~]# yum install -y vim-enhanced已加载插件:fastestmirror, langpacksLoading mir...

oschina130111 ⋅ 45分钟前 ⋅ 0

Deepin 操作系统四面楚歌

作为国内做的最好的 Linux 发行版,源自 Debian sid 的 Deepin 目前正面临重重困境,新版本不断延期,开发人员离职,bug 长期得不到修复,和 Debian/Ubuntu 的兼容性问题也面临越来越严重的挑...

六库科技 ⋅ 45分钟前 ⋅ 0

MyBatis之动态sql

我们需要知道的是,使用mybatis重点是对sql的灵活解析和处理。在原先的UserMappser.xml中,我们这样查询表中满足条件的记录 : 123 <select id="findUserList" parameterType="userQuery...

瑟青豆 ⋅ 46分钟前 ⋅ 0

这届俄罗斯世界杯的冷门那么多怎么办?

最纯粹的世界杯,最神奇的大冷门。 德国0比1被墨西哥摩擦了。 日本历史性的赢了哥伦比亚。 C罗也挑平了西班牙。 梅西被冰岛狮吼吼愣神了。 就连11次进世界杯4强的巴西也被瑞士逼平了。 天台已...

开源中国众包平台 ⋅ 47分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部