网络

教育改变生活

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1012|回复: 0
打印 上一主题 下一主题

什么是字符串常量池?

[复制链接]

271

主题

284

帖子

1243

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1243

最佳新人活跃会员热心会员突出贡献优秀版主

跳转到指定楼层
楼主
发表于 2019-8-2 11:42:50 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
请问什么是字符串常量池?

Java设计者为String提供了字符串常量池以提高其性能,那么字符串常量池的具体原理是什么,我们需要带着以下三个问题,去理解字符串常量池:

  • 字符串常量池的设计意图是什么?
  • 字符串常量池在哪里?
  • 如何操作字符串常量池?
字符串常量池的设计意图是什么?
字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。
JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化:

为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池;
如果字符串已经存在池中,就返回池中的实例引用;
如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享;
实现的基础:

因为字符串是不可变的,可以不用担心数据冲突进行共享;
运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用,这就意味着它们一直引用着字符串常量池中的对象,所以,在常量池中的这些字符串不会被垃圾收集器回收。
我们来看下面一段代码,就是从字符串常量池中获取相应的字符串:
String str1 = “hello”;
String str2 = “hello”;
System.out.printl("str1与str2比较地址" : str1 == str2 ) //true
System.out.printl("str1与str2比较内容" : str1.equals(str2) ) //true
/**说明:str1指向的“hello” 与 str2指向的“hello” 共用一块内存空间*/
字符串常量池在哪里?



在分析字符串常量池的位置时,首先得了解JVM内存模型,JVM内存区域分为线程共享区线程独占区
线程共享区包括 [堆] 和 [方法区]
线程独占区包括 [Java虚拟机栈]、[本地方法栈] 和 [陈程序计数器]

方法区:

存放加载的类信息、常量池、静态变量,静态代码块等信息;
类信息包括类的版本、字段、方法、接口等,方法区也被称为永久代。
程序计数器:

是一块比较小的内存区域,是唯一一个不会发生OutOfMemoryError的区域,可以这样理解方法进栈后,每一行代码都有一个标识,程序按着标识往下执行。
Java虚拟机栈:

每个方法执行,都会创建一个栈帧,方法调用进栈,方法结束出栈; 栈帧里面存放着局部变量表,操作数栈,动态链接以及方法出口等;
局部变量表里面存放着基本数据类型,引用类型等; 栈帧伴随着方法的开始而开始,结束而结束;
局部变量表所需的内存空间在编译期间就完成了分配,在运行期间是不会改变的;
栈很容易出现StackOverFlowError,栈内存溢出错误,常见于递归调用;
本地方法栈和Java虚拟机栈:

其实是差不多的,但是也是有区别的Java虚拟机栈为Java方法服务,本地方法栈为native方法服务
堆:

功能单一,就是存储对象的实例,堆其实又分新生代和老年代;
新生代又分Eden、Survivor01和Survivor02三个区域,垃圾收集器主要管理的区域,Eden区回收效率很高。
并不是所有的对象实例都会分配到堆上去,Java虚拟机栈也会分配。堆很容易出现OutOfMemoryError错误,内存溢出
如何操作字符串常量池?

JVM实例化字符串常量池时

String s1 = "Hello World";

String s2 = "Hello World";

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

String s4 = s2.intern();

System.out.println("s1 == s2? " + (s1 == s2)); // true

System.out.println("s1 == s3? " + (s1 == s3)); // false

System.out.println("s1 == s4? " + (s1 == s4)); // true

String.intern()判断这个常量是否存在于常量池。

如果存在{

  判断存在内容是引用还是常量{

     如果是引用{

      返回引用地址指向堆空间对象

     } 

     如果是常量{

      直接返回常量池常量

     }

  }

 }

 如果不存在{

   将当前对象引用复制到常量池,并且返回的是当前对象的引用

 }

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





回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

WEB前端

QQ|手机版|小黑屋|金桨网|助学堂  咨询请联系站长。

GMT+8, 2024-6-5 05:34 , Processed in 0.037620 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表