`
夏文权
  • 浏览: 237529 次
  • 性别: Icon_minigender_1
  • 来自: 贵州
社区版块
存档分类
最新评论

String内存分配情况解析

 
阅读更多

 

  •  String概述
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

    /**
     * Class String is special cased within the Serialization Stream Protocol.
     *
     * A String instance is written into an ObjectOutputStream according to
     * <a href="{@docRoot}/../platform/serialization/spec/output.html">
     * Object Serialization Specification, Section 6.2, "Stream Elements"</a>
     */
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

    /**
     * Initializes a newly created {@code String} object so that it represents
     * an empty character sequence.  Note that use of this constructor is
     * unnecessary since Strings are immutable.
     */
    public String() {
        this.value = "".value;
    }
}
 以上是jdk1.8中对String类的定义,看得出以下几个结论:
  1. String 类被final关键字修饰,因此String类不能被继承,并且它的成员方法都默认为final方法;字符串一旦创建就不能再修改。
  2. String 类实现了Serializable、CharSequence、 Comparable接口。
  3. String 实例的值是通过字符数组实现字符串存储的。
  • StringBuilder
       可变的字符序列,StringBuffer的append方法使用了synchronized,所以是线程安全的,StringBuilder的效率比StringBuffer快。
  • StringBuffer
       可变的字符序列,StringBuffer是线程不安全的。,StringBuffer的效率比StringBuffer慢。
  • "+"
  1.  在java中 当"+" 前后出现字符串时表示字符串拼接的操作,因为String是final修饰的,创建后就不可变,所以“+”底层实际是创建了一个StringBuilder对象,然后调用append方法去拼接字符串,再调用toString方法生成一个新的String。
  2. 只有使用引号包含文本的方式创建的String对象之间使用 “+” 连接产生的新对象才会被加入字符串池中。
        例如:String s5 = "a" + "b";直接在堆内存中的字符串池创建b这个对象
  1. 所有包含new方式新建对象(包括null)的 “+” 连接表达式,它所产生的新对象都不会被加入字符串池中。
      例如: 
               String s3 = new String("a");
              String s4 = new String("b");
              String s5 = s3 + s4;
 
 
  • intern方法
    /**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     * It follows that for any two strings {@code s} and {@code t},
     * {@code s.intern() == t.intern()} is {@code true}
     * if and only if {@code s.equals(t)} is {@code true}.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section 3.10.5 of the
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     */
    public native String intern();
      intern方法是一个本地方法。作用就是调用该方法时先去字符串常量池中查找是否有相等的(equals)值,有就直接返回常量池中的引用。没有就将该字符串存入字符串常量池,再返回字符串常量池中的引用。
注意:jkd1.6之前常量池和堆是分开的,所以这里是在常量池里存的的具体的值。jdk1.6之后常量池和堆在一起,这里常量池中保存的是堆中对象的引用。
 

  • String 创建对象在内存结构以及常见的对象数量问题解析
       创建对象有2种方式
  1. 通过常量池创建
        JVM会首先检查在字符串常量池,如果在字符串常量池中存在了该字符串 ,则将该字符串对象的地址值赋值给引用。如果字符串不在常量池中,就会在常量池中创建字符串,然后将字符串对象的地址值交给引用。所以最多只会创建一个对象。

     实例分析

     

String s1 = "a";
String s3 = new String("a");
String s4 = new String("b");
String s2 = "b";
String s5 = "a" + "b";
String s6 = "a" + s4;
String s7 = s1 + s2;
String s8 = "ab";
final String s9 = "e";
final String s10 = "f";
String s11 = s9 + s10;
String s12 = "ef";
System.out.println(s1 == s3);        // false
System.out.println(s1.equals(s3));	 // true
System.out.println(s5 == s6);		// false
System.out.println(s5 == s7);		// false
System.out.println(s5.equals(s7));	// true
System.out.println(s5 == s8);		// true
System.out.println(s11 == s12);		// true
    

 

     画图分析

    String内存分布和创建时对象生成情况等常见问题详解

    

     结果分析

     


s1 在常量池中生成了一个对象
s3 常量池中已经有了,所以只在堆中创建了一个对象
s4 常量池和堆中各创建了一个对象,共两个对象(这两个对象的字符串数组是同一个)
s2 常量池中已经有了,没有重新创建对象
s5 编译时将常量拼接好了,然后在常量池中创建了一个对象
s6 生成一个StringBuilder对象用来拼接,最后toSting在堆中生成一个新的对象,共两个对象
s7 与s6一样
s8 常量池中已经有了,没有重新创建对象
s9,s10都是在常量池中生成一个对象
s11 使用final修饰的变量,在编译时替换成常量了,然后将常量拼接好在常量池中创建了一个对象

 

 

  1. 通过new创建

        JVM会首先检查字符串常量池,如果字符串已经存在常量池中,直接复制堆中这个对象的副本,然后将堆中的地址值赋给引用,不会在字符串常量池中创建对象。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,然后在堆中复制该对象的副本,并将对象的地址值交给引用。所以可能生成一个或者两个对象。 

     实例分析

     

String s1 = new String("g");
s1.intern();
String s2 = "g";
System.out.println(s1 == s2);           // false
System.out.println(s1.intern() == s2);   // true

String s3 = new String("h") + new String("i");
s3.intern();
String s4 = "hi";
System.out.println(s3 == s4);           // true
System.out.println(s3.intern() == s4);   // true

String s5 = new String("j") + new String("k");
String s6 = "jk";
s5.intern();
System.out.println(s5 == s6);           // false
System.out.println(s5.intern() == s6);   // true
   

 

    画图分析

    String内存分布和创建时对象生成情况等常见问题详解

 

      结果分析

     


s1 在常量池中和堆中各创建了一个对象
s1.intern调用时发现常量池中已经存在“g”了,所以返回常量池中“g”的引用
s2 创建时发现常量池中已经存在了,不会生成新的对象,直接指向常量池的对象
s1和s2指向不同的对象,所以不相等。s1.intern指向常量池的对象,和s2相等
s3 在堆中生成两个匿名对象,一个StringBuilder对象,一个toString生成的对象,常量池中两个对象,共六个对象。最终生成的对象不会存放到字符串常量池中(参考上面“+”的定义)。
s3.intern调用时发现常量池中没有“hi”,将堆中“hi”对象的地址存放在常量池中,所以s3.intern最终还是指向堆中的对象。
s4 创建时发现常量池中已经存在了,直接返回常量池中的引用,所以s4最终也指向了堆中的“hi”对象。所以s3==s4,s3.intern() == s4
s5 创建和s3过程一样
s6 创建时发现常量池中没有,会在常量池中创建一个“jk”对象
s5.intern调用时,发现常量池中已经存在“jk”,直接返回常量池中的对象地址。
所以s5和s6是指向不同的对象

 

     

注明:本文参考和引用 "String内存分布和创建时对象生成情况等常见问题详解"这篇文章

“https://www.toutiao.com/i6981781517945111052/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1625616592&app=news_article&utm_source=weixin&utm_medium=toutiao_android&use_new_style=1&req_id=202107070809520101512082132E0BAAEA&share_token=74b9e806-66c2-4911-8763-3585b2b7a411&group_id=6981781517945111052”

   

 

分享到:
评论

相关推荐

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

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

    java内存分配和String类型的深度解析Java开发J

    java内存分配和String类型的深度解析Java开发Java经验技巧共12页.pdf.zip

    深入Java内存分配

    Java有几种存储区域? java内存分配 Java内存模型 Java内存分配实例解析 String 常量池问题 堆(Heap)和非堆(Non-heap)内存 堆内存分配 非堆内存分配

    空间内存的分配与回收

    空间内存的分配与回收 #include"stdio.h" #include"iostream.h" #include"string.h" #include"iomanip.h" const int MAXJOB=100;//定义表最大记录数 struct job{ int start; int length; char tag[20]; }; struct ...

    JVM内存分配及String常用方法解析

    主要介绍了JVM内存分配及String常用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    String a="hello" String b="hello" a==b 返回true的问题分析

    为什么会出现上面的情况呢? String a="hello world"; String b="hello world"; 通过上面的讲解可以知道,a和b都是指向常量池的同一个常量字符串"hello world"的,因此它们返回的地址是相同的。a和b都是引用...

    javascript内存分配原理实例分析

    本文实例讲述了javascript内存分配原理。分享给大家供大家参考,具体如下: JavaScript中的变量分为两种,原始值和引用值。原始值指的是原始数据类型的值,比如undefined,null,number,string,boolean类型所表示的值...

    Java内存分配分析/栈内存、堆内存

    首先学习JVM相关需要需要内存的组成。  基本内容  · 堆  java动态创建对象,即对于new的一个实例对象。但是需要注意的是该实例对象的成员变量都存储在各自的堆区域中,其中对象方法是在堆中共享,即不是每次...

    实例代码分析c++动态分配

    malloc后需要检查内存是否分配成功,free则要在指针不为空的情况下才能进行。 示例代码如下: #include #include #include &lt;string&gt; int main() { char *p = (char*)malloc(10); if ( p == NULL) { printf(...

    理解Javascript_01_理解内存分配原理分析

    原始值指的就是代表原始数据类型(基本数据类型)的值,即Undefined,Null,Number,String,Boolean类型所表示的值。 引用值指的就是复合数据类型的值,即Object,Function,Array,以及自定义对象,等等 栈和堆 与原始值...

    string_view-standalone:将C ++ 17'string_view'的自定义实现反向移植到c ++ 11

    与std::string不同, std::string为大多数字符串操作(例如substr )执行内存分配和复制, string_view仅观察而不修改条目。 这可以大大减少内存占用,并为不可变的字符串(例如解析和标记化)提供了较大的优化。 ...

    benchmark-memory:内存分析基准测试样式,适用于Ruby 2.1+

    基准内存 Benchmark-memory是一个工具,可以帮助您基准测试不同代码段的内存使用情况。 它利用为您提供一个块分配和保留的内存总量,以及分配和保留的对象和字符串数的指标。安装将此行添加到您的应用程序的Gemfile...

    理解Javascript

    01_理解内存分配在正式开始之前,我想先说两句,理解javascript系列博文是通过带领大家分析javascript执行时的内存分配情况,来解释javascript原理,具体会涵盖javascript预加载,闭包原理,面象对象,执行模型,...

    Java进阶教程解密JVM视频教程

    彻底分析 StringTable的相关知识与性能优化,掌握直接内存分配原理和释放手段。 * 在垃圾回收章节,不仅会介绍垃圾回收算法、分代垃圾回收机制,还会重点介绍 G1 垃圾回收器,辨析 Full GC 发生条件,jdk8以来对垃圾...

    JVM 知识点整理:对象的创建过程

    JVM 知识点整理:对象的创建过程类加载分配内存分配方式一:指针碰撞分配方式二:空闲列表如何选择?线程安全问题CAS + 失败重试方法线程本地分配缓存区(TLAB)后续工作 类加载 虚拟机遇到一条 new 指令时,首先...

    [java]读书笔记整理:一切都是对象

    然而,s指向的String对象仍然继续占据内存空间。在这一小段代码中,我们似乎无法再访问这个对象,因为对它唯一的引用已超过了作用域的范围。[后续在说,在程序执行过程中,怎样传递和赋值对象引用]。 事实证明,由...

    JavaScript对象拷贝与Object.assign用法实例分析

    在 JavaScript 中,对于基本数据类型(undefined、null、boolean、number、string)来说,在变量中存储的就是这个变量本身的值,复制是对值的复制,不存在深浅之说。但C系语言的共同特点中有,存储引用类型(对象)...

    微机实验报告.doc

    六、程序功能与调试过程中遇到的问题 1、分析程序功能 程序的功能是比较string1与string2所含字符是否相同,若相同则在屏幕上显示'MA TCH',否则,显示'NOT MATCH'。 因为string1为'Move the cursor backward',...

    (java se 代码)Bank Account Management System 银行账户管理子系统

    (Long id, String password, String repassword, String name, String personID, String email, int type) 返回类型:Account 项目需求规定账户类型:0 – 储蓄账户 1 – 信用账户 2 – 可贷款储蓄账户 3– 可贷款...

Global site tag (gtag.js) - Google Analytics