`

String:字符串常量池

    博客分类:
  • java
 
阅读更多

作为最基础的引用数据类型,Java 设计者为 String 提供了字符串常量池以提高其性能,那么字符串常量池的具体原理是什么,我们带着以下三个问题,去理解字符串常量池:

  • 字符串常量池的设计意图是什么?

  • 字符串常量池在哪里?

  • 如何操作字符串常量池?

字符串常量池的设计思想

  • 字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能

  • JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化

    • 为字符串开辟一个字符串常量池,类似于缓存区

    • 创建字符串常量时,首先坚持字符串常量池是否存在该字符串

    • 存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中

  • 实现的基础

    • 实现该优化的基础是因为字符串是不可变的,可以不用担心数据冲突进行共享

    • 运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用,这就意味着它们一直引用着字符串常量池中的对象,所以,在常量池中的这些字符串不会被垃圾收集器回收

代码:从字符串常量池中获取相应的字符串

  String str1 = “hello”;
  String str2 = “hello”;
  
  System.out.printl("str1 == str2" : str1 == str2 ) //true 

字符串常量池在哪里

在分析字符串常量池的位置时,首先了解一下堆、栈、方法区:

820e4d34-b89d-33a6-a776-4c088a08d2a9.png

    • 存储的是对象,每个对象都包含一个与之对应的class

    • JVM只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身

    • 对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定

    • 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象)

    • 每个栈中的数据(原始类型和对象引用)都是私有的

    • 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)

    • 数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会自动消失

  • 方法区

    • 静态区,跟堆一样,被所有的线程共享

    • 方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量

字符串常量池则存在于方法区

代码:堆栈方法区存储字符串

String str1 = “abc”;
String str2 = “abc”;
String str3 = “abc”;
String str4 = new String(“abc”);
String str5 = new String(“abc”);

798b04f9ga86a4ef61c5b&690

字符串对象的创建

面试题:String str4 = new String(“abc”) 创建多少个对象?

  1. 在常量池中查找是否有“abc”对象

    • 有则返回对应的引用实例

    • 没有则创建对应的实例对象

  2. 在堆中 new 一个 String("abc") 对象

  3. 将对象地址赋值给str4,创建一个引用

所以,常量池中没有“abc”字面量则创建两个对象,否则创建一个对象,以及创建一个引用

根据字面量,往往会提出这样的变式题:

String str1 = new String("A"+"B") ; 会创建多少个对象? 
String str2 = new String("ABC") + "ABC" ; 会创建多少个对象?

str1:
字符串常量池:"A","B","AB" : 3个
堆:new String("AB") :1个
引用: str1 :1个
总共 : 5个

str2 :
字符串常量池:"ABC" : 1个
堆:new String("ABC") :1个
引用: str2 :1个
总共 : 3个


代码:基础类型的变量和常量,变量和引用存储在栈中,常量存储在常量池中

int a1 = 1;
int a2 = 1;
int a3 = 1;

public static int INT1 =1 ;
public static int INT2 =1 ;
public static int INT3 =1 ;

798b04f9ga8e2c6779148&690


操作字符串常量池的方式

  • JVM实例化字符串常量池时

  String str1 = “hello”;
  String str2 = “hello”;
  
  System.out.printl("str1 == str2" : str1 == str2 ) //true
  • String.intern()

通过new操作符创建的字符串对象不指向字符串池中的任何对象,但是可以通过使用字符串的intern()方法来指向其中的某一个。java.lang.String.intern()返回一个保留池字符串,就是一个在全局字符串池中有了一个入口。如果以前没有在全局字符串池中,那么它就会被添加到里面

        // Create three strings in three different ways.
        String s1 = "Hello";
        String s2 = new StringBuffer("He").append("llo").toString();
        String s3 = s2.intern();
 
        // Determine which strings are equivalent using the ==
        // operator
        System.out.println("s1 == s2? " + (s1 == s2)); // false
        System.out.println("s1 == s3? " + (s1 == s3)); // true

补充:字面量和常量池初探

字符串对象内部是用字符数组存储的,那么看下面的例子:

    String m = "hello,world";
    String n = "hello,world";
    String u = new String(m);
    String v = new String("hello,world");
  1. 会分配一个11长度的char数组,并在常量池分配一个由这个char数组组成的字符串,然后由m去引用这个字符串

  2. 用n去引用常量池里边的字符串,所以和n引用的是同一个对象

  3. 生成一个新的字符串,但内部的字符数组引用着m内部的字符数组

  4. 同样会生成一个新的字符串,但内部的字符数组引用常量池里边的字符串内部的字符数组,意思是和u是同样的字符数组

使用图来表示的话,情况就大概是这样的(使用虚线只是表示两者其实没什么特别的关系):
string1.png
测试demo:

            String m = "hello,world";
            String n = "hello,world";
            String u = new String(m);
            String v = new String("hello,world");
            
            System.out.println(m == n); //true 
            System.out.println(m == u); //false
            System.out.println(m == v); //false
            System.out.println(u == v); //false 
    

结论:

  • m和n是同一个对象

  • m,u,v都是不同的对象

  • m,u,v,n但都使用了同样的字符数组,并且用equal判断的话也会返回true

分享到:
评论

相关推荐

    String int 字符串常量池 包装类型 函数参数 值传递引用传递 的 内存分配例子——源码

    String int 字符串常量池 包装类型 函数参数 值传递引用传递 的 内存分配例子——源码 public static void fun_ref (Ref_test ref_out){ Ref_test ref_in=new Ref_test(); ref_in.s1="in"; //ref_out.s1=...

    字符数组的存储方式 字符串常量池.docx

    为了避免每次都创建相同的字符串对象及内存分配,JVM内部对字符串对象的创建做了一定的优化,在Permanent Generation中专门有一块区域用来存储字符串常量池(一组指针指向Heap中的String对象的内存地址)。...

    Java String 字符串常量池解析

    主要介绍了Java String 字符串常量池解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    C#之CLR内存字符串常量池(string)

    C#中的string是比特殊的类,说引用类型,但不存在堆里面,而且String str=new String(“HelloWorld”)这样的重装也说没有的。 我们先来看一个方法: class Program { static void Main(string[] args) { String s...

    String_字符串.html

    String对象是不可变的。JVM对其做了一个优化,在内存中开辟了一段区域作为字符串常量池。通过"字面量"形式创建的字符串对象都会缓存并重用。

    String s = new String(” a “) 到底产生几个对象?

    老生常谈的一个梗,到2020了还在争论,你们一天天的,哎哎哎,我不是针对你一个,我是说...毕竟我和各位都是人才,java知识底蕴不能如此短浅,这题还没谢幕我们还能对面试官多哔哔几句:字符串常量池在不同版本的jvm中

    深入理解Java String#intern()内存模型

    大家知道,Java中string.intern()方法调用会先去字符串常量池中查找相应的字符串,如果字符串不存在,会在字符串常量池中创建该字符串然后再返回。  字符串常量池是一个固定大小的HashMap,桶的数量默认是1009, ...

    JVM系列之String.intern的性能解析

    String对象有个特殊的StringTable字符串常量池,为了减少Heap中生成的字符串的数量,推荐尽量直接使用String Table中的字符串常量池中的元素。 那么String.intern的性能怎么样呢?我们一起来看一下。 String.intern...

    Android代码-android-manifest-parser

    String Chunk // 字符串常量池 Chunk Type(0x001C0001) 4bytes // Chunk类型 Chunk Size 4bytes // Chunk长度 String Count 4bytes // 字符串个数 Style Count 4bytes Unknown 4bytes String Pool Offset 4...

    通过String.intern()方法浅谈堆中常量池

    主要介绍了通过String.intern()方法浅谈堆中常量池,在JDK7之前,字符串常量是存在永久带Perm 区的,JDK7开始在将常量池迁移到堆中,这个变化也导致了String的新特性,下面我们慢慢进行介绍。,需要的朋友可以参考下

    字符串String的定义与使用、字符串String与基本数据类型的相互转换

    String 常量 :和别的数据类型不一样,String类型的常量 和 对象一样,也拥有自己的 引用和实体,这些引用和实体都存放在常量池中; 例如: “你好”、”itm”、”my” ,这三个都是String 常量,它们分别拥有自己的...

    2Java SE(上).doc

    使用new关键字创建的字符串对象由于不会存入常量池也不会检查常量池,所以不会重用对象。 java编译器有一个优化措施,就是若计算表达式运算符两边都是字面量,那么编译器在生成class文件时就将结果计算完毕并保存...

    java中常量以及常量池

    1、举例说明 变量 常量 字面...  静态常量池:*.class文件中的常量池,class文件中的常量池不仅仅包含字符串,数值字面量,还包含类、方法的信息,占用class文件绝大部分空间。  运行时常量池:是jvm虚拟机在完成类装

    String类

    第一条语句会检查字符串常量池中是否存有“小明“这个字符串对象,如果有 则返回这个字符串的内存地址,如果没有则会在字符串常量池中创建这个对 象。 第二条语句 第二条语句同样也会检查字符串常量池中是否存有...

    Java语言程序设计(第3版)第06章-字符串.pptx

    6.1.1 字符串比较 Java语言程序设计(第3版) 不能使用"=="号来比较字符串内容是否相等 比较内容是否相等: boolean equals(String str) boolean equalsIgnoreCase(String str) s1 s2 Hello Hello 字符串常量池 s1 s2...

    Java基础篇——字符串处理(String,StringBuffer,StringBuild)

    提前说明本次的文章很长但是很有用,有耐心看完的必定不是凡人,必定会有很大的收获。本人在总结的过程中也收获了很多的知识,希望我们可以一起学习。...字符串常量池   String类常用的方法  StringBuffer

    jdk1.8之后的String.intern()方法内存分析

    1.字符串常量池划分 jvm对字符串常量池在不同jkd版本有不同的划分,这里用hotspot来分析,文章后部分会使用,主要有以下三种方式 大致划分为这几个部分,对方法区,元空间和堆等概念模糊的朋友可以参考方法区,永久...

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

    通过上面的讲解可以知道,a和b都是指向常量池的同一个常量字符串"hello world"的,因此它们返回的地址是相同的。a和b都是引用类型,相当于c语言里面的指针。java里面没有指针的概念,但是实际上引用变量里面放的确实...

    whx123#JavaHome#10. String类能被继承吗,为什么1

    1.字符串常量池的实现, 我们在代码中定义的"abc"等字符串很可能在常量池中存在, 每个String引用指向同一个字符串实现重用 2.安全和易用性的考虑, 在

Global site tag (gtag.js) - Google Analytics