一起来认识汇道Java常量池与字符串intern

合集下载

JavaString常量池

JavaString常量池

JavaString常量池
本周对Java字符串的理解更深了⼀步。

1. Java中的字符串是不可变类型,即在进⾏字符串加减操作时,每次都会重新返回⼀个新的对象,原来的对象仍然存储在字符串常量池
中。

2. 那么,什么是字符串常量池呢?字符串都存储在字符串常量池中,如果新建的⼀个字符串在常量池中已经存在,则直接返回其引⽤,
有如下操作:
点击查看代码
在新建字符串 b 时,由于字符串常量池中已经存在"abc",所以直接返回其索引,在输出System.out.println(a == b)时,输出的是true.使⽤字符串常量池,可以节省空间,获取效率也⽐较⾼。

3. ⽽new String()是另⼀种创建字符串的⽅式。

如果常量池中不存在该字符串,则会先在常量池中创建该字符串。

然后在堆内存中创建字符串对象,随后返回该字符串的引⽤,有如下操作:
点击查看代码
该⽅法输出false,因为new String()返回的是堆内存的地址,⽽字⾯创建字符串返回的是字符串常量池的地址。

4. 此外,字符串还存在intern⽅法,在执⾏该⽅法时,如果字符串常量池中不存在该字符串,则先创建,然后返回常量池中的引⽤。

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

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

深入理解 Java String#intern() 内存模型字符串常量池是一个固定大小的HashMap,桶的数量默认是1009, 从Java7u40开始,该默认值增大到60013。

在Java6当中,字符串常量池是放在Perm空间的,从Java7开始,字符串常量池被移到Heap空间。

下面,我们通过测试程序来窥探字符串常量池在Java6,Java7两个不同版本底下的内存分配情况。

测试程序public class StringPoolTest {public void testStringPoolWithLongString(){long i=0;while(true){String longString = "This is a very long stri ng, very very long string to test the gc behavior of the string constant pool"+i;longString.intern();i++;}}public static void main(String[] args){StringPoolTest stringPoolTest = new StringPoolTes t();stringPoolTest.testStringPoolWithLongString();}}测试程序很简单,一个死循环,循环里面通过递增变量i制造唯一的字符串,然后用main函数启动程序。

Java 6我们使用版本Jdk1.6.0_29来跑该程序,打开Java VisualVM监控,可以看到,Perm 区不断发生GC,由此的出结论,虽然字符串常量池放在Perm空间,但当Perm 空间接近满的时候,JVM会将字符串常量池中的无用字符串回收掉。

Java 7下面,我们切换到Jdk1.7.0_67重跑该程序,可以看到Perm区内存分配曲线很平滑,没有出现内存分配的现象。

java面试题之----String的intern

java面试题之----String的intern

java⾯试题之----String的internWhen---什么时候需要了解String的intern⽅法:⾯试的时候(蜜汁尴尬)!虽然不想承认,不过⾯试的时候经常碰到这种⾼逼格的问题来考察我们是否真正理解了String的不可变性、String常量池的设计以及String.intern⽅法所做的事情。

但其实,我们在实际的编程中也可能碰到可以利⽤String.intern⽅法来提⾼程序效率或者减少内存占⽤的情况,这个我们等下会细说。

What---String.intern⽅法究竟做了什么:Returns a canonical representation for the string object. A pool of strings, initially empty, is maintained privately by the class String. When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned. It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true. All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java? Language Specification.上⾯是jdk源码中对intern⽅法的详细解释。

Java字符串常量池详解

Java字符串常量池详解

Java字符串常量池详解文章目录•••oo•••oo概念在JVM中,为了减少字符串对象的重复创建,维护了一块特殊的内存空间,这块内存空间就被称为字符串常量池。

在JDK1.6及之前,字符串常量池存放在方法区中。

到JDK1.7之后,就从方法区中移除了,而存放在堆中。

一下是《深入理解虚Java 虚拟机》第二版原文:对于HotSpot 虚拟机,根据官方发布的路线图信息,现在也有放弃永久代并逐步改为采用Native Memory来实现方法区的规划了,在目前已经发布的JDK1.7 的HotSpot中,已经把原本放在永久代的字符串常量池移出。

根据查阅的资料显示在JDK1.7以后的版本中字符串常量池移到堆内存区域;同时在jdk1.8中移除整个永久代,取而代之的是一个叫元空间(Metaspace)的区域。

用法解析1. 直接使用双引号声明出来的String对象String s1 = "abc"创建过程:JVM会使用常量池来管理字符串直接量。

在执行这句话时,JVM会先检查常量池中是否已经存有"abc",若没有则将"abc"存入常量池,否则就复用常量池中已有的"abc",将其引用赋值给变量a。

2. 使用new方法创建出来的String对象可以使用String提供的intern方法。

String s2 = new String("abc");在执行这句话时,JVM会先使用常量池来管理字符串直接量,String 先使用 intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中,即将"abc"存入常量池。

然后再创建一个新的String对象,这个对象会被保存在堆内存中。

并且,堆中对象的数据会指向常量池中的直接量。

例如下面代码进行对abc常量进行检测是否存在的测试:String s1="abc";String s2 = new String("abc");System.out.println(s2.intern());测试案例运行如下代码:public class Test{public class void main(String[] args){String s1 = "abc";String s2 = "abc";System.out.println("s1="+s1);//abcSystem.out.println(s1 == s2);//trueSystem.out.println("=================");char[] charArray = {'a','b','c'};String s3 = new String(charArray);System.out.println("s3="+s3);//abcSystem.out.println(s1 == s3);//falseSystem.out.println("=================");String s4 = new String("abc");System.out.println("s4="+s4);//abcSystem.out.println(s1==s4);System.out.println("=================");}}运行结果如下:虽然两个输出两个字符串的结果相同,都为abc,但比较两者时则不同,这是因为比较的规则为:•引用类型比较时,比较的是其地址值;•基本数据类型比较的是其数据值。

Java中的常量池(字符串常量池、class常量池和运行时常量池)

Java中的常量池(字符串常量池、class常量池和运行时常量池)

Java中的常量池(字符串常量池、class常量池和运⾏时常量池)转载。

https:///zm130********/article/details/77534349简介:这⼏天在看Java虚拟机⽅⾯的知识时,看到了有⼏种不同常量池的说法,然后我就去CSDN、博客园等上找资料,⾥⾯说的内容真是百花齐放,各⾃争艳,因此,我好好整理了⼀下,将我⾃认为对的理解写下来与⼤家共同探讨:在Java的内存分配中,总共3种常量池:1.字符串常量池(String Constant Pool):1.1:字符串常量池在Java内存区域的哪个位置?在JDK6.0及之前版本,字符串常量池是放在Perm Gen区(也就是⽅法区)中;在JDK7.0版本,字符串常量池被移到了堆中了。

⾄于为什么移到堆内,⼤概是由于⽅法区的内存空间太⼩了。

1.2:字符串常量池是什么?在HotSpot VM⾥实现的string pool功能的是⼀个StringTable类,它是⼀个Hash表,默认值⼤⼩长度是1009;这个StringTable在每个HotSpot VM的实例只有⼀份,被所有的类共享。

字符串常量由⼀个⼀个字符组成,放在了StringTable上。

在JDK6.0中,StringTable的长度是固定的,长度就是1009,因此如果放⼊String Pool中的String⾮常多,就会造成hash冲突,导致链表过长,当调⽤String#intern()时会需要到链表上⼀个⼀个找,从⽽导致性能⼤幅度下降;在JDK7.0中,StringTable的长度可以通过参数指定:-XX:StringTableSize=666661.3:字符串常量池⾥放的是什么?在JDK6.0及之前版本中,String Pool⾥放的都是字符串常量;在JDK7.0中,由于String#intern()发⽣了改变,因此String Pool中也可以存放放于堆内的字符串对象的引⽤。

java intern方法

java intern方法

Java intern方法1. 什么是intern方法1.1 intern方法的定义Java中的String类中提供了一个intern方法,用于将字符串添加到字符串常量池(String Pool)中,并返回字符串在常量池中的引用。

1.2 常量池的概念常量池是Java运行时数据区的一部分,用于存储在编译时期生成的各种字面量和符号引用。

其中,字符串常量池(String Pool)是常量池中的一个重要部分,用于存储字符串对象。

1.3 intern方法的作用•intern方法能够节省内存空间,提高程序的运行效率。

•使用intern方法可以判断两个字符串是否相等。

2. intern方法的使用2.1 如何调用intern方法在Java中,我们可以通过在字符串对象上调用intern()方法来使用该方法。

例如:String str1 = "Hello";String str2 = new String("Hello");String str3 = str2.intern();2.2 intern方法的返回值•如果常量池中已经存在一个等于该字符串的引用,则直接返回该引用。

•如果常量池中不存在这个字符串,则在常量池中创建一个新的字符串,并返回该引用。

2.3 intern方法与字符串比较在Java中,字符串比较通常使用equals方法。

然而,对于使用intern方法的字符串,我们可以直接使用”==“运算符进行比较,因为intern方法返回的是常量池中的引用。

例如,我们可以使用以下代码来比较字符串:String str1 = "Hello";String str2 = new String("Hello");String str3 = str2.intern();System.out.println(str1 == str2); // falseSystem.out.println(str1 == str3); // trueSystem.out.println(str2 == str3); // false3. intern方法的原理3.1 常量池的工作原理在Java虚拟机启动时,会创建一个称为PermGen的内存区域来存储常量池。

java中字符串的intern方法

java中字符串的intern方法
在Java中,字符串是不可变的对象。

为了提高字符串处理的效率,Java使用了字符串池(String Pool)的概念。

字符串池是一个存放字符串常量的池子,其中相同的字符串常量只需要存储一次,多次
使用时只需要在池子中查找即可。

在这个字符串池中,每个字符串常量都是唯一的,即每个字符串
常量在 JVM 中只有一份。

而使用new关键字创建的字符串对象,都是
存在堆内存中的,不会放入字符串池中,因此比较时可能会产生误判,而使用intern方法可以比较字符串常量,在字符串池中查找是否存在
相同的字符串常量,如果存在,则直接返回池中的字符串常量,否则
将该字符串常量存入池中,并返回该字符串常量的引用。

例如,以下代码:
```java
String a = "hello";
String b = "hello";
String c = new String("hello");
System.out.println(a == b); // 输出true
System.out.println(a == c); // 输出false
System.out.println(a == c.intern()); // 输出true
```
其中,a和b都是直接定义的字符串常量,因此它们的引用指向
的是字符串池中的同一份对象;而c是使用new关键字创建的字符串
对象,因此其引用指向的是堆内存中的一个新对象,所以a和c的比
较结果为false,但使用intern方法可以将c放入字符串池中并返回
对应的引用,与a比较则返回true。

string 类的intern方法

string 类的intern方法string类的intern方法是Java中非常重要的一个方法,它可以用于字符串的常量池处理。

在本文中,我们将深入探讨这个方法的原理、用法以及它的一些注意事项。

我们来了解一下什么是字符串常量池。

在Java中,字符串常量池是一块特殊的内存区域,用于存储字符串常量。

当我们创建一个字符串常量时,虚拟机会首先在常量池中查找是否存在相同内容的字符串,如果存在,则返回常量池中的引用;如果不存在,则将该字符串添加到常量池中并返回引用。

这种机制可以有效地节省内存空间,提高程序的性能。

而intern方法就是用来实现字符串常量池处理的。

该方法的作用是:如果常量池中已经存在该字符串,则返回常量池中的引用;如果常量池中不存在该字符串,则将该字符串添加到常量池中并返回引用。

简单来说,intern方法可以将一个堆中的字符串对象转移到字符串常量池中,并返回常量池中的引用。

那么,我们该如何使用intern方法呢?在实际开发中,我们可以通过调用字符串对象的intern方法来实现。

例如:```javaString str1 = new String("hello");String str2 = str1.intern();```在上面的代码中,我们首先创建了一个字符串对象"hello",然后通过调用intern方法将该字符串对象转移到了字符串常量池中,并将常量池中的引用赋值给了str2。

这样就可以实现字符串常量池处理了。

需要注意的是,intern方法是一个native方法,它的实现是由Java虚拟机提供的。

在实际使用中,我们应该遵循以下几点注意事项:1. 避免过多使用intern方法:由于字符串常量池是一个全局共享的区域,频繁地调用intern方法可能会导致性能问题。

因此,我们应该在必要的情况下才使用intern方法,避免滥用。

2. 小心字符串对象的创建:由于字符串对象是不可变对象,每次创建都会在堆中产生新的对象。

字符串intern方法

字符串intern方法
在Java中,字符串池是一种特殊的数据结构,它用于存储和管理字符串对象。

当我们创建一个字符串对象时,它被存储在堆内存中,但是当我们调用intern方法时,它会将该字符串对象添加到字符串池中,并返回字符串池中对象的引用。

如果字符串池中已经存在该字符串对象,则intern方法只返回该字符串对象的引用,而不会创建新的对象。

字符串池的主要优点是它可以减少内存占用,因为相同的字符串只需要存储一次。

此外,它还提高了字符串比较的效率,因为比较时只需要比较字符串对象的引用,而不需要比较字符串的内容。

需要注意的是,虽然intern方法可以减少内存占用,但是它也可能会增加内存占用,因为字符串池中的字符串对象不会被垃圾回收器清理,除非没有任何对象引用它们。

因此,使用intern方法时需要谨慎,避免创建大量无用的字符串对象导致内存泄漏。

总之,字符串的intern方法可以帮助我们管理字符串对象,减少内存占用和提高比较效率,但是需要注意使用时的内存占用问题。

- 1 -。

深入浅出Java基础——字符串常量池

深⼊浅出Java基础——字符串常量池StringConstantPool(jdk8)1.字符串常量池是什么字符串常量池⽤于存储编译期间存在的所有字符串实例的引⽤,以及运⾏时动态添加的引⽤。

字符串常量池是全局的,只有⼀个。

当我们以String str = "123"形式创建字符串实例时,⾸先会去判断字符串常量池中是否有引⽤指向相同内容的实例,如果有则返回该实例。

否则在堆中创建 String 对象并将引⽤驻留在字符串常量池中。

2.为什么要有字符串常量池的存在想象现在有这样⼀个场景:⼀个内容为 username 的字符串需要经常使⽤在登录时,没有字符串常量池的情况下,如果要实现数据的共享,可以通过将这个字符串声明为全局常量(static final)的⽅式,并通过⼀个哈希表对这些常量进⾏统⼀管理。

如果直接不实现数据共享,每次有⽤户登录,再次使⽤该字符串时都去创建⼀个新的对象,那就浪费太多时间以及空间了。

众所周知,偷懒是科技发展的动⼒。

这个时候有个⼤佬想:能否直接在内存中将可能经常使⽤的字符串统⼀管理,实现数据的共享呢?这样就可以少打些代码了~ 于是⼀个享元模式的实例:全局字符串常量池诞⽣了。

这⾥还需要提出相关的两个常量池 1. Class ⽂件中的常量池 2.运⾏时常量池1.Class ⽂件常量池:存储了字⾯量以及符号引⽤字⾯量:⽂本字符串,例如类中有这样⼀⾏代码private String str = "123",那么常量池中会出现 str、123、 Ljava/lang/String(类的描述⽅式:L + 全限定名)等字⾯量。

符号引⽤:包含 1.类和接⼝的全限定名 2.字段的简单名称及描述符 3.⽅法的简单名称及描述符(本⽂不作探讨)2.运⾏时常量池运⾏时常量池在 jdk8 ,位于元空间内。

⽤于存储从 class ⽂件中读取的信息,包括常量池。

当类被加载时,虚拟机会将 Class ⽂件中的静态数据转化为运⾏时常量池中的运⾏时数据。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

作为目前计算机编程语言排行榜第一名的高级语言,Java在计算机科学知识时代扮演了重要的角色。

全球有超过500万Java开发者活跃在世界的每一个角落,数以亿万计的Web用户每次上网都亲历Java的威力和感受其魅力,吸引着越来越多的互联网爱好者加入Java培训队伍中。

汇道科技坚持“成就学生的心一百年不变”的理念,打造出最适合中国企业需求的Java人才培养体系。

Java课程是汇道科技的第一大课程方向,为学员打造最人性化的Java课程。

下面就跟着汇道科技一起来认识Java常量池与字符串intern。

在Java应用程序运行时,Java虚拟机会保存一份内部的运行时常量池,它区别于class文件的常量池,是class文件常量池映射到虚拟机中的数据结构。

1.CONSTANT_Class入口解析
数组类的符号解析较为特殊。

若是基本类型数组,那么虚拟机将创建该基本类型的新数组类,并创建一个Class实例来代表该类型,数组类的定义类加载器为启动类加载器。

若是引用类型的数组,那么在此之前还会进行引用类型的解析,数组类的定义类加载器为引用类型的定义类加载器。

非数组类和接口的的解析将经历以下步骤:
(1) 加载该类型和其所有的超类型
如果该类型在此之前已经装载到了虚拟机的当前命名空间,那么直接使用已经被装载的类型即可,否则由引用的发起类的初始类加载器进行加载。

对目标类型的超类的加载必然是在对当前类型加载完的基础上进行的,因为只有加载完当前类型,才能从class文件的super_class域找到其直接超类的符号引用,再递归进行解析和加载,直至ng.Object类。

而在递归返回的过程中,会检查interfaces域以查看实现或扩展了哪些接口,并再次递归遍历对接口的符号引用。

(2)检查访问权限
随后是对目标类型的连接和初始化,这样才可以正常使用该类型。

前面提到,对目标类型的初始化需要其所有超类都必须进行初始化(超接口不是必须的),并且,由于已经对其超类进行了加载,所以不必再依赖于自该类向Object类的解析顺序,而是从Object类向该类进行初始化。

类型的连接和初始化步骤如下:
(3)类型校验
(4)类型准备
(5)类型解析(可推迟)
注意该过程是对被引用类型及其超类的符号引用的解析,因为对于被引用类型的某些符号引用不会立刻用到,故该步骤之前是严格意义上属于发起引用的类型的符号解析的过程。

只有在主动使用被引用类型的这些符号引用所指向的类型时,才会对这些符号引用进行解析,对其所指向的类型进行装载、连接和初始化。

(6)类型初始化
2.CONSTANT_Fieldref入口解析
由于一个类型不会含有其超类型所定义的字段,所以对目标字段的搜索将会从字段所指向的类型开始,从该类型开始搜索,再递归搜索其所实现或扩展的接口,再递归搜索其超类,直至找到目标字段,并会将运行时常量池的该字段入口标记为已解析,并在该常量池的数据上改为对这个字段的直接引用。

3.CONSTANT_Methodref入口解析
与字段的搜索类似但有所不同,其搜索顺序将从该类型开始,再递归搜索其超类,在递归搜索其所实现或扩展的接口。

4.CONSTANT_InterfaceMethodRef入口解析
对接口方法的搜索就是从被解析的接口开始,向其超接口递归搜索。

5.CONSTANT_String入口解析
Java虚拟机会将字符串处理为一个字符串对象加以维护,而虚拟机所维护的就是一张字符串池,它包含所有被”拘留”的字符串对象的引用。

对CONSTANT_String常量池的解析首先就要查看字符串池中该字符串对象的引用是否存在,如果存在则直接把常量池数据解析为该字符串对象的引用,若不存在,那么就需要根据这个字符串序列创建一个字符串对象,并将其引用加入到字符串池中,并将常量池数据解析为该引用。

也可以使用String对象的intern对象来拘留一个字符串(注意并非字符串对象),若该字符串池中存在对该字符串序列的对象的引用,那么直接返回该引用即可,否则,将
会拘留该字符串,但注意拘留返回的字符串对象引用将不会指向原String对象,因为原 String对象位于Java堆,而字符串池的对象是虚拟机所创建的,由虚拟机所维护。

package com.ice.intern;
public class InternTest {
public static void main(String args[]){
String a = new String("123");
String b = a;
String c = new String("123");;
System.out.println("before intern:");
System.out.println("a = b ? :" + (a == b));
System.out.println("a = c ? :" + (a == c));
a = a.intern();
c = c.intern();
System.out.println("after intern:");
System.out.println("a = b ? :" + (a == b));
System.out.println("a = c ? :" + (a == c));
}
}
结果如下:
(6).其他类型(数据基本类型)入口解析
直接使用常量池所包含的常量值即可
6.直接引用
常量池解析最终将符号引用替换成为直接引用。

指向类型、类变量和类方法的直接引用可能为在方法区的指针。

而指向实例变量和实例方法的直接引用是从对象映像的开始到该实例变量或方法表的偏移。

实例变量的组织方式为:从Object类开始到该实例的类型,将类中声明的实例变量按在class文件中出现的顺序依次放在对象映像中。

实例方法的组织方式较为类似:从Object类开始到该实例的类型,将类中声明的实例方法指针按在class文件中出现的顺序依次放在对象映像中。

但对于重写的方法将出现在超类对应的位置(该方法第一次出现的位置)。

但是访问接口方法就不能简单地通过方法表的偏移量来进行访问,而必须搜索对象的类的方法表来找到该方法。

比如Factory接口分别由A和B来实现其produce()方法,但由于A和B不能保证由同一个实现了Factory接口的超类派生,即有着同样的produce()方法偏移,那么就无法通过方法表的偏移来访问Factory的produce()方法。

7.装载约束
对于一个类型指向另一个类型的符号引用,如果引用的类型和被引用类型并非由同一个初始加载器加载(可能通过用户自定义ClassLoader来实现),那么虚拟机就必须确保被引用类型在不同的命名空间中保持一致。

这样就通过自定义ClassLoader来加载不受信类型后,就不会发生解析对被引用类型的符号引用时,把受信的类型当做已经被解析过的不受信类型(因为对方法的符号引用只有权限定名和描述符,并不会也无法得知其初始类加载器),从而调用了不受信类型的方法访问受信类型的受保护成员。

相关文档
最新文档