精华内容
下载资源
问答
  • 不耻下问:Java语言字符串常量和字符串常量池的作用是什么?可以节约内存么,是什么意思
  • 经常在网上各大版块都能看到对于java字符串运行时内存分配的探讨,形如:String a = "123",String b = new String("123"),这两种形式的字符串是存放在什么地方的呢,其实这两种形式的字符串字面值"123"本身在运行时...

    经常在网上各大版块都能看到对于java字符串运行时内存分配的探讨,形如:String a = "123",String b = new String("123"),这两种形式的字符串是存放在什么地方的呢,其实这两种形式的字符串字面值"123"本身在运行时既不是存放在栈上,也不是存放在堆上,他们是存放在方法区中的某个常量区,并且对于相同的字符串字面值在内存中只保留一份。下面我们将以实例来分析。

    1.==运算符作用在两个字符串引用比较的两个案例:

    public classStringTest {public static voidmain(String[] args) {//part 1

    String s1 = "i love china";

    String s2= "i love china";

    System.out.println("result:" +s1 ==s2);//程序运行结果为true//part 2

    String s3 = new String("i love china");

    String s4= new String("i love china");

    System.out.println("result:" +s3 ==s4);//程序运行结果为false

    }

    }

    我们知道java中==运算符比较的是变量的值,对于引用类型对应的变量的值存放的是引用对象的地址,在这里String是引用类型,这里面的四个变量的值存放的其实是指向字符串的地址。对于part2的执行结果是显然的,因为new操作符会使jvm在运行时在堆中创建新的对象,两个不同的对象的地址是不同的。但是由part1的执行结果,可以看出s1和s2是指向的同一个地址,那么由变量s1,s2指向的字符串是存放在什么地方的呢,jvm又是对字符串如何处理的呢。同样的对于变量s3,s4所指向的堆中的不同的字符串对象,他们会分别在自己的对象空间中保存一份"i love china"字符串吗,为了了解jvm是如何处理字符串,首先我们看java编译器生成的字节码指令。通过字节码指令我们来分析jvm将会执行哪些操作。

    2.以下为程序生成的部分字节码信息。红色标注的是我们需要关注的部分。

    Constant pool:

    #1 = Class #2 //StringTest

    #2 =Utf8 StringTest

    #3 = Class #4 //java/lang/Object

    #4 = Utf8 java/lang/Object

    #5 = Utf8 #6 =Utf8 ()V

    #7 =Utf8 Code

    #8 = Methodref #3.#9 //java/lang/Object."":()V

    #9 = NameAndType #5:#6 //"":()V

    #10 =Utf8 LineNumberTable

    #11 =Utf8 LocalVariableTable

    #12 = Utf8 this#13 =Utf8 LStringTest;

    #14 =Utf8 main

    #15 = Utf8 ([Ljava/lang/String;)V

    #16 = String #17 //i love china 字符串地址的引用

    #17 =Utf8 i love china

    #18 = Fieldref #19.#21 //java/lang/System.out:Ljava/io/PrintStream;

    #19 = Class #20 //java/lang/System

    #20 = Utf8 java/lang/System

    #21 = NameAndType #22:#23 //out:Ljava/io/PrintStream;

    #22 =Utf8 out

    #23 = Utf8 Ljava/io/PrintStream;

    #24 = Class #25 //java/lang/StringBuilder

    #25 = Utf8 java/lang/StringBuilder

    #26 = String #27 //result:

    #27 =Utf8 result:

    #28 = Methodref #24.#29 // java/lang/StringBuilder."":(Ljava/lang/String;)V

    #29 = NameAndType #5:#30 // "":(Ljava/lang/String;)V

    #30 = Utf8 (Ljava/lang/String;)V

    #31 = Methodref #24.#32 // java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;

    #32 = NameAndType #33:#34 // append:(Z)Ljava/lang/StringBuilder;

    #33 = Utf8 append

    #34 = Utf8 (Z)Ljava/lang/StringBuilder;

    #35 = Methodref #24.#36 // java/lang/StringBuilder.toString:()Ljava/lang/String;

    #36 = NameAndType #37:#38 // toString:()Ljava/lang/String;

    #37 = Utf8 toString

    #38 = Utf8 ()Ljava/lang/String;

    #39 = Methodref #40.#42 // java/io/PrintStream.println:(Ljava/lang/String;)V

    #40 = Class #41 // java/io/PrintStream

    #41 = Utf8 java/io/PrintStream

    #42 = NameAndType #43:#30 // println:(Ljava/lang/String;)V

    #43 = Utf8 println

    #44 = Class #45 // java/lang/String

    #45 = Utf8 java/lang/String

    #46 = Methodref #44.#29 // java/lang/String."":(Ljava/lang/String;)V

    #47 = Utf8 args

    #48 = Utf8 [Ljava/lang/String;

    #49 = Utf8 s1

    #50 = Utf8 Ljava/lang/String;

    #51 = Utf8 s2

    #52 = Utf8 s3

    #53 = Utf8 s4

    #54 = Utf8 StackMapTable

    #55 = Class #48 // "[Ljava/lang/String;"

    #56 = Utf8 SourceFile

    #57 = Utf8 StringTest.java

    ...........

    //对应的方法的字节码指令,由jvm运行时解释执行。public static voidmain(java.lang.String[]);

    descriptor: ([Ljava/lang/String;)V

    flags: ACC_PUBLIC, ACC_STATIC

    Code:

    stack=4, locals=5, args_size=1

    0: ldc #16 //String i love china,该指令是将常量池的#16处符号引用,在这里为字符串“ilove china”符号引用push到栈顶。该指令与底下的指令2对应于程序中的String s1 = "i love china"语句

    2: astore_1 //将栈顶的对象引用赋值给局部变量1.3: ldc #16 //String i love china,同0处的指令,指向的是同一个符号引用处的常量。该指令与底下的指令5对应于程序中的 String s2 = "i love china"语句。

    5: astore_2 //将栈顶的对象引用赋值给局部变量2.6: getstatic #18 //Field java/lang/System.out:Ljava/io/PrintStream;

    9: new #24 //class java/lang/StringBuilder

    12: dup13: ldc #26 //String result:

    15: invokespecial #28 //Method java/lang/StringBuilder."":(Ljava/lang/String;)V

    18: aload_119: aload_220: if_acmpne 27 //弹出栈顶两个对象引用进行比较其是否相等,不等,转到指令27处,执行,相等执行下一条指令

    23: iconst_124: goto 28

    27: iconst_028: invokevirtual #31 //Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;

    31: invokevirtual #35 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

    34: invokevirtual #39 //Method java/io/PrintStream.println:(Ljava/lang/String;)V

    37: new #44 //class java/lang/String,创建一个对象,该对象位于常量池#44引用处,这里为String对象,并将对象引用push到栈顶。

    40: dup //拷贝栈顶一份对象引用push到栈顶。41: ldc #16 //String i love china,同0,3处指令。

    43: invokespecial #46 //Method java/lang/String."":(Ljava/lang/String;)V

    46: astore_3 47: new #44 //class java/lang/String//创建一个对象,并将对象引用push到栈顶

    50: dup51: ldc #16 //String i love china, 将字符串的符号引用push到栈顶。

    53: invokespecial #46 //Method java/lang/String."":(Ljava/lang/String;)V,根据栈顶的对应的对象引用及字符串引用调用对象的init初始化方法,对字符串对象初始化

    56: astore 4 //将栈顶对象引用赋值给变量4.

    58: getstatic #18 //Field java/lang/System.out:Ljava/io/PrintStream;

    61: new #24 //class java/lang/StringBuilder

    64: dup65: ldc #26 //String result:

    67: invokespecial #28 //Method java/lang/StringBuilder."":(Ljava/lang/String;)V

    70: aload_371: aload 4

    73: if_acmpne 80

    76: iconst_177: goto 81

    80: iconst_081: invokevirtual #31 //Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;

    84: invokevirtual #35 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

    87: invokevirtual #39 //Method java/io/PrintStream.println:(Ljava/lang/String;)V

    90:return

    .........

    LineNumberTable:

    line 7: 0

    line 8: 3

    line 9: 6

    line 11: 37

    line 12: 47

    line 13: 58

    line 14: 90

    LocalVariableTable:

    Start Length Slot Name Signature

    0 91 0 args [Ljava/lang/String;//局部变量0

    3 88 1 s1 Ljava/lang/String;//局部变量1

    6 85 2 s2 Ljava/lang/String;//局部变量2

    47 44 3 s3 Ljava/lang/String;//局部变量3

    58 33 4 s4 Ljava/lang/String;//局部变量4

    字节码中红色的部分是与我们讨论相关的。通过生成的字节码,我们可以对示例程序得出如下结论。

    1).java编译器在将程序编译成字节码的过程中,对遇到的字符串常量"i love china"首先判断其是否在字节码常量池中存在,不存在创建一份,存在的话则不创建,也就是相等的字符串,只保留一份,通过符号引用可以找到它,这样使得程序中的字符串变量s1和s2都是指向常量池中的同一个字符串常量。在运行时jvm会将字节码常量池中的字符串常量存放在方法区中的通常称之为常量池的位置,并且字符串是以字符数组的形式通过索引来访问的。jvm在运行时将s1与s2指向的字符串相对引用地址指向字符串实际的内存地址。

    2).对于String s3 = new String("i love china"),String s4 = new String("i love china"),由字节码可以看出其是调用了new指令,jvm会在运行时创建两个不同的对象,s3与s4指向的是不同的对象地址。所以s3==s4比较的结果为false。

    其次,对于s3与s4对象的初始化,从字节码看出是调用对象的init方法并且传递的是常量池中”i love china”的引用,那么创建String对象及初始化究竟干了什么,我们可以查看通过查看String的源码及String对象生成的字节码,以便更好的了解对于new String("i love china")时,在对象内部是做了字符串的拷贝还是直接指向该字符串对应的常量池的地址的引用。

    3.String对象的部分源码:

    public final class String

    implements java.io.Serializable, Comparable, 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

    public String() {

    this.value = new char[0];

    }

    public String(String original) {

    this.value = original.value;

    this.hash = original.hash;

    }

    从源码中我们看到String类里有个实例变量 char value[],通过构造方法我们可知,对象在初始化时并没有做拷贝操作,只是将传递进来的字符串对象的地址引用赋给了实例变量value。由此我们可以初步的得出结论:即使使用new String("abc")创建了一个字符串对象时,在内存堆中为该对象分配了空间,但是在堆上并没有存储"abc"本身的任何信息,只是初始化了其内部的实例变量到"abc"字符串的引用。其实这样做也是为了节省内存的存储空间,以及提高程序的性能。

    4.下面我们来看看String对象部分字节码信息:

    publicjava.lang.String();

    descriptor: ()V

    flags: ACC_PUBLIC

    Code:

    stack=2, locals=1, args_size=1

    0: aload_01: invokespecial #1 //Method java/lang/Object."":()V

    4: aload_05: iconst_06: newarray char

    8: putfield #2 //Field value:[C

    11: returnLineNumberTable:

    line137: 0line138: 4line139: 11

    publicjava.lang.String(java.lang.String);

    descriptor: (Ljava/lang/String;)V

    flags: ACC_PUBLIC

    Code:

    stack=2, locals=2, args_size=2

    0: aload_0 //将局部变量0push到栈顶,自身对象的引用。1: invokespecial #1 //Method java/lang/Object."":()V 弹出栈顶对象引用调用该对象的#1处的初始化方法。

    4: aload_0 //将自身对象引用push到栈顶。5: aload_1 //传递的字符串引用push到栈顶。6: getfield #2 //Field value:[C //弹出栈顶的字符串引用并将其赋值给#2处的实例变量,并将其存放到栈上。

    9: putfield #2 //Field value:[C // 弹出栈顶的字符串引用及对象自身的引用并将字符串的引用赋值给本对象自身的实例变量。

    12: aload_013: aload_114: getfield #3 //Field hash:I

    17: putfield #3 //Field hash:I

    20: return

    从字节码的角度我们可以得出结论,new String("abc")在构造新对象时执行的是字符串引用的赋值,而不是字符串的拷贝。以上是从源码及字节码的角度来对字符串的内存分配进行的分析与总结。

    展开全文
  • java字符串存储在Java堆内存中的字符串池。Stringjava中的特殊类,可以使用new运算符创建String对象,也可以用双引号提供值。1. Java字符串池下面一个图表,清楚地解释了如何在Java堆空间中维护字符串池,...

    java字符串池是存储在Java堆内存中的字符串池。String是java中的特殊类,可以使用new运算符创建String对象,也可以用双引号提供值。

    1. Java字符串池

    下面是一个图表,清楚地解释了如何在Java堆空间中维护字符串池,以及当使用不同的方法创建字符串时会发生什么。

    dc63664596c87d56cd54e0f8e28486c7.png

    字符串池有助于为Java Runtime节省大量空间,尽管创建String需要更多时间。

    当使用双引号创建一个String时,它首先在String池中查找具有相同值的String,如果存在那么只是返回引用,否则它在池中创建一个新String,然后返回引用。

    但是,使用new运算符,那么将强制String类在堆空间中创建一个新的String对象。可以使用intern()方法将它放入池中,或者从具有相同值的字符串池中引用另一个String对象。

    下面是String池的java示例程序:

    public class StringPool {

    /**

    * Java String Pool example

    * @param args

    */

    public static void main(String[] args) {

    String s1 = "Cat";

    String s2 = "Cat";

    String s3 = new String("Cat");

    System.out.println("s1 == s2 :"+(s1==s2));

    System.out.println("s1 == s3 :"+(s1==s3));

    }

    }

    执行上面示例示例代码,得到以下结果 -

    s1 == s2 :true

    s1 == s3 :false

    字符串池中创建了多少个字符串?

    有时在java面试中,会被问到字符串池的问题。例如,在下面的语句中创建了多少个字符串?

    String str = new String("Cat");

    在上面的语句中,将创建1或2个字符串。如果池中已存在字符串文字 - Cat,则池中只会创建一个字符串 - str。如果池中没有字符串文字 - Cat,那么它将首先在池中创建,然后在堆空间中创建,因此将创建总共2个字符串对象。

    ¥ 我要打赏

    纠错/补充

    收藏

    加QQ群啦,易百教程官方技术学习群

    注意:建议每个人选自己的技术方向加群,同一个QQ最多限加 3 个群。

    展开全文
  • 经常在网上各大版块都能看到对于java字符串运行时内存分配的探讨,形如:String a = "123",String b = new String("123"),这两种形式的字符串是存放在什么地方的呢,其实这两种形式的字符串字面值"123"本身在运行时...

    经常在网上各大版块都能看到对于java字符串运行时内存分配的探讨,形如:String a = "123",String b = new String("123"),这两种形式的字符串是存放在什么地方的呢,其实这两种形式的字符串字面值"123"本身在运行时既不是存放在栈上,也不是存放在堆上,他们是存放在方法区中的某个常量区,并且对于相同的字符串字面值在内存中只保留一份。下面我们将以实例来分析。

    1.==运算符作用在两个字符串引用比较的两个案例:

    public class StringTest {

    public static void main(String[] args) {

    //part 1

    String s1 = "i love china";

    String s2 = "i love china";

    System.out.println("result:" + s1 == s2);//程序运行结果为true

    //part 2

    String s3 = new String("i love china");

    String s4 = new String("i love china");

    System.out.println("result:" + s3 == s4);//程序运行结果为false

    }

    }

    我们知道java中==运算符比较的是变量的值,对于引用类型对应的变量的值存放的是引用对象的地址,在这里String是引用类型,这里面的四个变量的值存放的其实是指向字符串的地址。对于part2的执行结果是显然的,因为new操作符会使jvm在运行时在堆中创建新的对象,两个不同的对象的地址是不同的。但是由part1的执行结果,可以看出s1和s2是指向的同一个地址,那么由变量s1,s2指向的字符串是存放在什么地方的呢,jvm又是对字符串如何处理的呢。同样的对于变量s3,s4所指向的堆中的不同的字符串对象,他们会分别在自己的对象空间中保存一份"i love china"字符串吗,为了了解jvm是如何处理字符串,首先我们看java编译器生成的字节码指令。通过字节码指令我们来分析jvm将会执行哪些操作。

    2.以下为程序生成的部分字节码信息。红色标注的是我们需要关注的部分。

    Constant pool:

    #1 = Class #2 // StringTest

    #2 = Utf8 StringTest

    #3 = Class #4 // java/lang/Object

    #4 = Utf8 java/lang/Object

    #5 = Utf8

    #6 = Utf8 ()V

    #7 = Utf8 Code

    #8 = Methodref #3.#9 // java/lang/Object."":()V

    #9 = NameAndType #5:#6 // "":()V

    #10 = Utf8 LineNumberTable

    #11 = Utf8 LocalVariableTable

    #12 = Utf8 this

    #13 = Utf8 LStringTest;

    #14 = Utf8 main

    #15 = Utf8 ([Ljava/lang/String;)V

    #16 = String #17 // i love china 字符串地址的引用

    #17 = Utf8 i love china

    #18 = Fieldref #19.#21 // java/lang/System.out:Ljava/io/PrintStream;

    #19 = Class #20 // java/lang/System

    #20 = Utf8 java/lang/System

    #21 = NameAndType #22:#23 // out:Ljava/io/PrintStream;

    #22 = Utf8 out

    #23 = Utf8 Ljava/io/PrintStream;

    #24 = Class #25 // java/lang/StringBuilder

    #25 = Utf8 java/lang/StringBuilder

    #26 = String #27 // result:

    #27 = Utf8 result:

    #28 = Methodref #24.#29 // java/lang/StringBuilder."":(Ljava/lang/String;)V

    #29 = NameAndType #5:#30 // "":(Ljava/lang/String;)V

    #30 = Utf8 (Ljava/lang/String;)V

    #31 = Methodref #24.#32 // java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;

    #32 = NameAndType #33:#34 // append:(Z)Ljava/lang/StringBuilder;

    #33 = Utf8 append

    #34 = Utf8 (Z)Ljava/lang/StringBuilder;

    #35 = Methodref #24.#36 // java/lang/StringBuilder.toString:()Ljava/lang/String;

    #36 = NameAndType #37:#38 // toString:()Ljava/lang/String;

    #37 = Utf8 toString

    #38 = Utf8 ()Ljava/lang/String;

    #39 = Methodref #40.#42 // java/io/PrintStream.println:(Ljava/lang/String;)V

    #40 = Class #41 // java/io/PrintStream

    #41 = Utf8 java/io/PrintStream

    #42 = NameAndType #43:#30 // println:(Ljava/lang/String;)V

    #43 = Utf8 println

    #44 = Class #45 // java/lang/String

    #45 = Utf8 java/lang/String

    #46 = Methodref #44.#29 // java/lang/String."":(Ljava/lang/String;)V

    #47 = Utf8 args

    #48 = Utf8 [Ljava/lang/String;

    #49 = Utf8 s1

    #50 = Utf8 Ljava/lang/String;

    #51 = Utf8 s2

    #52 = Utf8 s3

    #53 = Utf8 s4

    #54 = Utf8 StackMapTable

    #55 = Class #48 // "[Ljava/lang/String;"

    #56 = Utf8 SourceFile

    #57 = Utf8 StringTest.java

    ...........

    //对应的方法的字节码指令,由jvm运行时解释执行。

    public static void main(java.lang.String[]);

    descriptor: ([Ljava/lang/String;)V

    flags: ACC_PUBLIC, ACC_STATIC

    Code:

    stack=4, locals=5, args_size=1

    0: ldc #16 // String i love china,该指令是将常量池的#16处符号引用,在这里为字符串“ilove china”符号引用push到栈顶。该指令与底下的指令2对应于程序中的String s1 = "i love china"语句

    2: astore_1 //将栈顶的对象引用赋值给局部变量1.

    3: ldc #16 // String i love china,同0处的指令,指向的是同一个符号引用处的常量。该指令与底下的指令5对应于程序中的 String s2 = "i love china"语句。

    5: astore_2 //将栈顶的对象引用赋值给局部变量2.

    6: getstatic #18 // Field java/lang/System.out:Ljava/io/PrintStream;

    9: new #24 // class java/lang/StringBuilder

    12: dup

    13: ldc #26 // String result:

    15: invokespecial #28 // Method java/lang/StringBuilder."":(Ljava/lang/String;)V

    18: aload_1

    19: aload_2

    20: if_acmpne 27 //弹出栈顶两个对象引用进行比较其是否相等,不等,转到指令27处,执行,相等执行下一条指令

    23: iconst_1

    24: goto 28

    27: iconst_0

    28: invokevirtual #31 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;

    31: invokevirtual #35 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

    34: invokevirtual #39 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

    37: new #44 // class java/lang/String,创建一个对象,该对象位于常量池#44引用处,这里为String对象,并将对象引用push到栈顶。

    40: dup //拷贝栈顶一份对象引用push到栈顶。

    41: ldc #16 // String i love china,同0,3处指令。

    43: invokespecial #46 // Method java/lang/String."":(Ljava/lang/String;)V

    46: astore_3

    47: new #44 // class java/lang/String//创建一个对象,并将对象引用push到栈顶

    50: dup

    51: ldc #16 // String i love china, 将字符串的符号引用push到栈顶。

    53: invokespecial #46 // Method java/lang/String."":(Ljava/lang/String;)V,根据栈顶的对应的对象引用及字符串引用调用对象的init初始化方法,对字符串对象初始化

    56: astore 4 //将栈顶对象引用赋值给变量4.

    58: getstatic #18 // Field java/lang/System.out:Ljava/io/PrintStream;

    61: new #24 // class java/lang/StringBuilder

    64: dup

    65: ldc #26 // String result:

    67: invokespecial #28 // Method java/lang/StringBuilder."":(Ljava/lang/String;)V

    70: aload_3

    71: aload 4

    73: if_acmpne 80

    76: iconst_1

    77: goto 81

    80: iconst_0

    81: invokevirtual #31 // Method java/lang/StringBuilder.append:(Z)Ljava/lang/StringBuilder;

    84: invokevirtual #35 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

    87: invokevirtual #39 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

    90: return

    .........

    LineNumberTable:

    line 7: 0

    line 8: 3

    line 9: 6

    line 11: 37

    line 12: 47

    line 13: 58

    line 14: 90

    LocalVariableTable:

    Start Length Slot Name Signature

    91 0 args [Ljava/lang/String;//局部变量0

    88 1 s1 Ljava/lang/String; //局部变量1

    85 2 s2 Ljava/lang/String;//局部变量2

    44 3 s3 Ljava/lang/String;//局部变量3

    33 4 s4 Ljava/lang/String;//局部变量4

    字节码中红色的部分是与我们讨论相关的。通过生成的字节码,我们可以对示例程序得出如下结论。

    1).java编译器在将程序编译成字节码的过程中,对遇到的字符串常量"i love china"首先判断其是否在字节码常量池中存在,不存在创建一份,存在的话则不创建,也就是相等的字符串,只保留一份,通过符号引用可以找到它,这样使得程序中的字符串变量s1和s2都是指向常量池中的同一个字符串常量。在运行时jvm会将字节码常量池中的字符串常量存放在方法区中的通常称之为常量池的位置,并且字符串是以字符数组的形式通过索引来访问的。jvm在运行时将s1与s2指向的字符串相对引用地址指向字符串实际的内存地址。

    2). 对于String s3 = new String("i love china"),String s4 = new String("i love china"),由字节码可以看出其是调用了new指令,jvm会在运行时创建两个不同的对象,s3与s4指向的是不同的对象地址。所以s3==s4比较的结果为false。

    其次,对于s3与s4对象的初始化,从字节码看出是调用对象的init方法并且传递的是常量池中”i love china”的引用,那么创建String对象及初始化究竟干了什么,我们可以查看通过查看String的源码及String对象生成的字节码,以便更好的了解对于new String("i love china")时,在对象内部是做了字符串的拷贝还是直接指向该字符串对应的常量池的地址的引用。

    3.String对象的部分源码:

    public final class String

    implements java.io.Serializable, Comparable, 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

    public String() {

    this.value = new char[0];

    }

    public String(String original) {

    this.value = original.value;

    this.hash = original.hash;

    }

    从源码中我们看到String类里有个实例变量 char value[],通过构造方法我们可知,对象在初始化时并没有做拷贝操作,只是将传递进来的字符串对象的地址引用赋给了实例变量value。由此我们可以初步的得出结论:即使使用new String("abc")创建了一个字符串对象时,在内存堆中为该对象分配了空间,但是在堆上并没有存储"abc"本身的任何信息,只是初始化了其内部的实例变量到"abc"字符串的引用。其实这样做也是为了节省内存的存储空间,以及提高程序的性能。

    4.下面我们来看看String对象部分字节码信息:

    public java.lang.String();

    descriptor: ()V

    flags: ACC_PUBLIC

    Code:

    stack=2, locals=1, args_size=1

    0: aload_0

    1: invokespecial #1 // Method java/lang/Object."":()V

    4: aload_0

    5: iconst_0

    6: newarray char

    8: putfield #2 // Field value:[C

    11: return

    LineNumberTable:

    line 137: 0

    line 138: 4

    line 139: 11

    public java.lang.String(java.lang.String);

    descriptor: (Ljava/lang/String;)V

    flags: ACC_PUBLIC

    Code:

    stack=2, locals=2, args_size=2

    0: aload_0 //将局部变量0push到栈顶,自身对象的引用。

    1: invokespecial #1 // Method java/lang/Object."":()V 弹出栈顶对象引用调用该对象的#1处的初始化方法。

    4: aload_0 //将自身对象引用push到栈顶。

    5: aload_1 //传递的字符串引用push到栈顶。

    6: getfield #2 // Field value:[C // 弹出栈顶的字符串引用并将其赋值给#2处的实例变量,并将其存放到栈上。

    9: putfield #2 // Field value:[C // 弹出栈顶的字符串引用及对象自身的引用并将字符串的引用赋值给本对象自身的实例变量。

    12: aload_0

    13: aload_1

    14: getfield #3 // Field hash:I

    17: putfield #3 // Field hash:I

    20: return

    从字节码的角度我们可以得出结论,new String("abc")在构造新对象时执行的是字符串引用的赋值,而不是字符串的拷贝。以上是从源码及字节码的角度来对字符串的内存分配进行的分析与总结。

    以上这篇java 字符串内存分配的分析与总结(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

    展开全文
  • 起步三种内部表示Unicode字符串每个字符 1 个字节(Latin-1)每个字符 2 个字节(UCS-2)每个字符 4 个字节(UCS-4)# Include/unicodeobject.htypedef uint32_t Py_UCS4;typedef uint16_t Py_UCS2;typedef uint8_t Py_UCS...

    起步

    三种内部表示Unicode字符串

    每个字符 1 个字节(Latin-1)

    每个字符 2 个字节(UCS-2)

    每个字符 4 个字节(UCS-4)

    # Include/unicodeobject.h

    typedef uint32_t Py_UCS4;

    typedef uint16_t Py_UCS2;

    typedef uint8_t Py_UCS1;

    # Include/cpython/unicodeobject.h

    typedef struct {

    PyCompactUnicodeObject _base;

    union {

    void *any;

    Py_UCS1 *latin1;

    Py_UCS2 *ucs2;

    Py_UCS4 *ucs4;

    } data; /* Canonical, smallest-form Unicode buffer */

    } PyUnicodeObject;

    为什么内部不采用 utf8 进行编码

    字符串驻留机制

    空字符串 '' 及所有;

    变量名;

    参数名;

    字符串常量(代码中定义的所有字符串);

    字典键;

    属性名称;

    void PyUnicode_InternInPlace(PyObject **p)

    {

    PyObject *s = *p;

    PyObject *t;

    if (s == NULL || !PyUnicode_Check(s))

    return;

    // 对PyUnicodeObjec进行类型和状态检查

    if (!PyUnicode_CheckExact(s))

    return;

    if (PyUnicode_CHECK_INTERNED(s))

    return;

    // 创建intern机制的dict

    if (interned == NULL) {

    interned = PyDict_New();

    if (interned == NULL) {

    PyErr_Clear(); /* Don't leave an exception */

    return;

    }

    }

    // 对象是否存在于inter中

    t = PyDict_SetDefault(interned, s, s);

    // 存在, 调整引用计数

    if (t != s) {

    Py_INCREF(t);

    Py_SETREF(*p, t);

    return;

    }

    /* The two references in interned are not counted by refcnt.

    The deallocator will take care of this */

    Py_REFCNT(s) -= 2;

    _PyUnicode_STATE(s).interned = SSTATE_INTERNED_MORTAL;

    }

    字符串缓冲池

    static PyObject *unicode_latin1[256] = {NULL};

    [unicodeobjec.c]

    PyObject * PyUnicode_DecodeUTF8Stateful(const char *s,

    Py_ssize_t size,

    const char *errors,

    Py_ssize_t *consumed)

    {

    ...

    /* ASCII is equivalent to the first 128 ordinals in Unicode. */

    if (size == 1 && (unsigned char)s[0] < 128) {

    return get_latin1_char((unsigned char)s[0]);

    }

    ...

    }

    总结

    展开全文
  • 字符串内容一旦定义了就不能改变。每次改变都会重新开辟一个内存空间存储新的内容。 字符串创建对象(赋值)有两种方式: 通过构造方法创建对象。...在栈中开辟mian方法空间,然后实例化字符串 s1,s1实例化...
  • 该函数会将指定字符串追加到目的字符串的结尾,所以必须保证目的字符串有足够的内存空间来容纳两个字符串,否则会导致溢出错误。C语言中字符串连接函数strcat(),该函数用来将两个字符串连接(拼接)起来。(推荐教程...
  • 但关键这么区分有什么意义。毕竟不可变序列和可变序列的划分对+=操作符和id的作用有所不同。 作者说其中扁平序列存放的值而不是引用,它在内存中一段连续的内存空间。 但是经过测试,。 &gt;&gt;&gt...
  • 前言之前写过一篇关于JVM内存区域划分的文章,但是昨天接到蚂蚁金服的面试,问到JVM相关的内容,解释一下JVM的内存区域划分,这部分答得还不错,但是后来又问了Java里面String存放的位置,之前只记得String一个...
  • 前言之前写过一篇关于JVM内存区域划分的文章,但是昨天接到蚂蚁金服的面试,问到JVM相关的内容,解释一下JVM的内存区域划分,这部分答得还不错,但是后来又问了Java里面String存放的位置,之前只记得String一个...
  • 什么是字符串数组

    2019-02-12 09:25:00
    1.C语言中没有字符串类型,用一片内存空间保存一串字符,这片空间称作字符数组。 2.以‘\0’结尾的字符数组被称为字符串数组。‘\0’是字符串结束的标志。 3.用双引号括起来的都是字符串。 二、初始化 char ...
  • 这两种形式的字符串是存放在什么地方的呢,其实这两种形式的字符串字面值"123"本身在运行时既不是存放在栈上,也不是存放在堆上,他们是存放在方法区中的某个常量区,并且对于相同的字符串字面值在内存中只保留一份...
  • 前言在Java中,字符串是一个不可变的类,一个不可变的类指的是它的实例对象不能被修改,所有关于这个对象的信息在这个对象被创建时已初始化且不能被改变。不可变类有很多优势,这篇文章总结了字符串类之所以被设计成...
  • 2.list中打印的确数组类型的字符串,原因list类继承自下图中的AbstractList类, 该类又继承了AbstractCollection类,该类重写了toString方法.返回的数组类型的字符串,如下图所示: 因此,list也继承了...
  • 一、字符串一个有序的字符的集合,用于存储和表示基本的文本信息,一对单、双、或三引号中间包含的内容称之为字符串:1、创建: s = “hello word”2、特性:有序、不可变。有序:有顺序的集合,就像列表的索引...
  • Python 变量类型变量存储在内存中的值。...Python 字符串字符串是 Python 中最常用的数据类型。我们可以使用引号('或")来创建字符串。创建字符串很简单,只要为变量分配一个值即可。例如:var1 =...
  • 从第三章数据类型可知 Java 语言中并没有...字符串实例既然字符串是个 String 类实例,那么每初始化一个字符串变量就会在堆中创建一个字符串对象,并同时在栈中声明一个变量值指向堆中的对象。初始化几个字符串:...
  • 在Java中String一个类,所以除了上面直接让String对象等于一个字符串之外,我们可以向普通的类一样,使用new创建一个String对象; 当我们使用第一种方法也就是String string = ”hello”;的时候,程序会在特定的...
  • 想到啥写啥,今天说一说什么是字符串常量池!!! 概念:字符串常量池JVM为了提升性能和减少内存开销,避免字符串的重复创建,所以开辟出来一个单独的内存空间,就是字符串池,字符串常量池由String类私有的...
  • Download.java:244行的代码:text=text.replaceAll("","");出错信息:Exceptioninthread"Thread-1"java.lang.OutOfMemoryError:Javaheapspaceatjava.ut...Download.java:244行的代码:text=text.replaceAll("","");...
  • 所谓不可变对象,指一个对象在创建后,它的内部状态不会被改变的对象。这意味着当我们将一个不可变对象的引用赋值给某个变量后,我们就...节省内存字符串常量池:字符串常量池JVM中的一块特殊区域(1.7之前存放在...
  • 结论:在Java中,内容相同的字串常量(“Hello”)只保存一份以节约内存,所以s0,s1,s2实际上引用的同一个对象。编译器在编译s2一句时,会去掉“+”号,直接把两个字串连接起来得一个字串(“Hello”)。这种优化...
  • java字符串内存表示

    2018-02-02 17:27:44
    字符串是java是最常用的类也是特殊对待的类型之一,字符串是定常的无论一个串它存储在内存中的什么位置它的内容一经创建就不可改变,平时我们感觉到串被修改了这只是一种错觉 ,实现上串的操作是丢弃了旧串而生成了...
  • 变量变量:把一个值赋值给一个名字时,它会储存在内存中,称之为变量(virable);但在python中并不会储存在变量里,仅仅类似于给值加了标签变量的...变量的命名尽量专业字符串字符串区别于其他数字类型的标志在于两...
  • 字符串内存分析

    2017-09-26 20:04:33
    String a=&quot;a&quot;; String b=new String(&quot;a&quot;...上面两段创建字符串的代码, System....第一个比的地址,第二个比的才是字符串里的内容。那又为什么地址会不相等呢。String b=new Str
  • 问题:字符串常量池分配在什么内存区域? 方法区?永久区? 堆内存?首先明晰几个概念:方法区 - java虚拟机规范的运行时数据区方法区 概念属于 java虚拟机规范的运行时数据区的一部分. 运行时数据区包括: 程序计数器、...
  • Java字符串常量池是什么?顾名思义,Java中的字符串常量池(String Pool)是存储在Java堆内存中的字符串池。我们知道String是java中比较特殊的类,我们可以使用new运算符创建String对象,也可以用双引号(”“)创建...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,458
精华内容 2,183
关键字:

内存字符串是什么