Java设计者为String提供了字串常量池以提高其效能,那么字串常量池的具体原理是什么,我们需要带着以下三个问题,去理解字串常量池:
字串常量池的设计意图是什么?字串常量池在哪里?如何操作字串常量池?字串常量池的设计意图是什么?
字串的分配,和其他的物件分配一样,耗费高昂的时间与空间代价。
JVM为了提高效能和减少内存开销,在例项化字串常量的时候进行了一些优化:
为了减少在JVM中建立的字串的数量,字串类维护了一个字串池,每当程式码建立字串常量时,JVM会首先检查字串常量池;
如果字串已经存在池中,就返回池中的例项引用;
如果字串不在池中,就会例项化一个字串并放到池中。Java能够进行这样的优化是因为字串是不可变的,可以不用担心资料冲突进行共享;
实现的基础:
因为字串是不可变的,可以不用担心资料冲突进行共享;
执行时例项建立的全域性字串常量池中有一个表,总是为池中每个唯一的字串物件维护一个引用,这就意味着它们一直引用着字串常量池中的物件,所以,在常量池中的这些字串不会被垃圾收集器回收。
我们来看下面一段程式码,就是从字串常量池中获取相应的字串:
字串常量池在哪里?
在分析字串常量池的位置时,首先得了解JVM内存模型,JVM内存区域分为执行绪共享区和执行绪独占区。
执行绪共享区包括 [堆] 和 [方法区]
执行绪独占区包括 [Java虚拟机器栈]、[本地方法栈] 和 [陈程式计数器]
方法区:
存放载入的类资讯、常量、静态变数,静态程式码块等资讯;
类资讯包括类的版本、字段、方法、界面等,方法区也被称为永久代。
程式计数器:
是一块比较小的内存区域,是唯一一个不会发生OutOfMemoryError的区域,可以这样理解方法进栈后,每一行程式码都有一个标识,程式按著标识往下执行。
Java虚拟机器栈:
每个方法执行,都会建立一个栈帧,方法呼叫进栈,方法结束出栈; 栈帧里面存放着区域性变量表,算子栈,动态连结以及方法出口等;
区域性变量表里面存放着基本资料型别,引用型别等; 栈帧伴随着方法的开始而开始,结束而结束;
区域性变量表所需的内存空间在编译期间就完成了分配,在执行期间是不会改变的;
栈很容易出现StackOverFlowError,栈内存溢位错误,常见于递回呼叫;
本地方法栈和Java虚拟机器栈:
其实是差不多的,但是也是有区别的Java虚拟机器栈为Java方法服务,本地方法栈为native方法服务
堆:
功能单一,就是储存物件的例项,堆其实又分新生代和老年代;
新生代又分Eden、Survivor01和Survivor02三个区域,垃圾收集器主要管理的区域,Eden区回收效率很高。
并不是所有的物件例项都会分配到堆上去,Java虚拟机器栈也会分配。堆很容易出现OutOfMemoryError错误,内存溢位
如何操作字串常量池?
JVM例项化字串常量池时
String.intern()判断这个常量是否存在于常量池。
如果存在{
判断存在内容是引用还是常量{
如果是引用{
返回引用地址指向堆空间物件
}
如果是常量{
直接返回常量池常量
}
}
}
如果不存在{
将当前物件引用复制到常量池,并且返回的是当前物件的引用
}
通过new操作符建立的字串物件不指向字串池中的任何物件,但是可以通过使用字串的intern()方法来指向其中的某一个。java.lang.String.intern()返回一个保留池字串,就是一个在全域性字串池中有了一个入口。如果以前没有在全域性字串池中,那么它就会被新增到里面。