浅谈String.intern()方法

更新时间:2023-04-22 19:22:21 阅读: 评论:0


2023年4月22日发(作者:自作自受造句)

浅谈()⽅法

类型“==”⽐较样例代码如下:

package ;

public class StringTest {

public static void main(String[] args) {

String str1 = "todo";

String str2 = "todo";

String str3 = "to";

String str4 = "do";

String str5 = str3 + str4;

String str6 = new String(str1);

n("------普通String测试结果------");

("str1 == str2 ? ");

n( str1 == str2);

("str1 == str5 ? ");

n(str1 == str5);

("str1 == str6 ? ");

(str1 == str6);

n();

n("---------intern测试结果---------");

("() == () ? ");

n(() == ());

("() == () ? ");

n(() == ());

("() == () ? ");

n(() == ());

("str1 == () ? ");

n(str1 == ());

}

}

代码运⾏结果如下所⽰:

------普通String测试结果------

str1 == str2 ? true

str1 == str5 ? fal

str1 == str6 ? fal

---------intern测试结果---------

() == () ? true

() == () ? true

() == () ? true

str1 == () ? true

普通String代码结果分析:Java语⾔会使⽤常量池保存那些在编译期就已确赵高简介 定的已编译的class⽂件中的⼀份数据。主要有类、接⼝、

⽅法中的常量,以及⼀些以⽂本形式出现的符号引⽤,如类和接⼝的全限定名、字段的名称和描述符、⽅法和名称和描述符等。因此有关花的成语 在编译

完Intern类后,⽣成的class⽂件中会在常量池中保存“todo”、“to”和“do”三个String常量。变量str1和str2均保存的是常量池

中“todo”的引⽤,所以str1==str2成⽴;在执⾏ str5 = str3 + s飞龙肉 tr4这句时,JVM会先创建⼀个StringBuilder对象,通过

()⽅法将str3与str4的值拼接,然后通过ng()返回⼀个堆中的String对象的引⽤,赋值给

str5,因此str1和str5指向的不是同⼀个String对象,str1 == str5不成⽴;String str6 = new String(str1)⼀句显式创建了⼀个新的

String对象,因此str1 == str6不成⽴便是显⽽易见的事了。

()使⽤原理

()是⼀个Native⽅法,底层调⽤C++的 StringTable::intern⽅法实现。当通过语句()调⽤intern()⽅法

后,JVM 就会在当前类的常量池中查找是否存在与str等值的St委婉的近义词 ring,若存在则直接返回常量池中相应Strnig的引⽤;若不存在,则会在常

量池中创建⼀个等值的String,然后返回这个String在常量池中的引⽤。因此,只要是等值的String对象,使⽤intern()⽅法返回的都是常

量池中同⼀个String引⽤,所以,这些等值的String对象通过intern()后使⽤==是可以匹配的。由此就雪景照片 可以理解上⾯代码中------intern-----

-部分的结果了。因为str1、str5和str6是三个等值的String,所以通过intern()⽅法,他们均会指向常量池中的同⼀个String引⽤,因此

() == () == ()均为true。

() in JDK6

Jdk6中常量池位于PermGen(永久代)中,PermGen是⼀块主要⽤于存放已加载的类信息和字符串池的⼤⼩固定的区域。执⾏

intern()⽅法时,若常量池中不存在等值的字符串,JVM就会在常量池中创建⼀个等值的字符串,然后返回该字符串的引⽤。除此以

外,JVM 会⾃动在常量池中保存⼀份之前已使⽤过的字符串集合。Jdk6中使⽤intern()⽅法的主要问题就在于常量池被保存在PermGen

中:⾸先,PermGen是⼀块⼤⼩固定的区域,⼀般不同的平台PermGen的默认⼤⼩也不相同,⼤致在32M到96M之间。所以不能对不受

控制的运⾏时字符串(如⽤户输⼊信息等)使⽤intern()⽅法,否则很有可能会引发PermGen内存溢出;其次String对象保存在Java堆

区,Java堆区与PermGen是物理隔离的,因此如果对多个不等值的字符串对象执⾏intern操作,则会导致内存中存在许多重复的字符串,

会造成性能损失。

() in JDK7

Jdk7将常量池从PermGen区移到了Java堆区,执⾏intern操作时,如果常量池已经存在该字符串,则直接返回字符串引⽤,否则复制

该字符串对象的引⽤到常量池中并返回。堆区的⼤⼩⼀般不受限,所以将常量池从PremGen区移到堆区使得常量池的使⽤不再受限于固定

⼤⼩。除此之外,位于堆区的常量池中的对象可以被垃圾回收。当常量池中的字符串不再存在指向它的引⽤时,JVM就会回收该字符串。可

以使⽤ -XX:StringTableSize 虚拟机参数设置字符串池的map⼤⼩。字符串池内部实现为⼀个HashMap,所以当能够确定程序中需要

intern的字符串数⽬时,可以将该map的size设置为所需数⽬*2(减少hash冲突),这样就可以使得()每次都只需要常量时间

和相当⼩的内存就能够将⼀个String存⼊字符串池中。

()适⽤场景

Jdk6中常量池位于PermGen区,⼤⼩受限,所以不建议适⽤intern()⽅法,当需要字符串池时,需要⾃⼰使⽤HashMap实现。

Jdk7、8中,常量池由PermGen区移到了堆区,还可以通过-XX:StringTableSize参数设置StringTable的⼤⼩,常量池的使⽤不再受限,

由此可以重新考虑使⽤intern()⽅法。intern()⽅法优点:执⾏速度⾮常快,直接使⽤==进⾏⽐较要⽐使⽤equals()⽅法快很多;内存占

⽤少。虽然intern()⽅法的优点看上去很诱⼈,但若不是在恰当的场合中使⽤该⽅法的话,便⾮但不能获得如此好处,反⽽还可能会有性能

损失。下⾯程序对⽐了使⽤intern()⽅法和未使⽤intern()⽅法存储100万个String时的性能,从输出结果可以看出,若是单纯使⽤intern()

⽅法进⾏数据存储的话,程序运⾏时间要远⾼于未使⽤int核桃木 ern()⽅法时:

public class InternTest {

public static void main(String[] args) {

print("noIntern: " + noIntern());

print("intern: " + intern());

}

private static long noIntern(){

long start = tTimeMillis();

for (int i = 0; i < 1000000; i++) {

int j = i % 100;

String str = f(j);

}

return tTimeMillis() - start;

}

private static long intern(){

long start = tTimeMillis();

for (int i = 0; i < 1000000; i++) {

int j = i % 100;

String str = f(j).intern();

}

return tTimeMillis() - start;

}

}

程序运⾏结果:

noIntern: 48 // 未使⽤intern⽅法时,存储100万个String所需时间

intern: 99 // 使⽤intern⽅法时,存储100万个String所需时间

由于intern()操作每次都需要与常量池中的数据进⾏⽐较以查看常量池中是否存在等值数据,同时JVM需要确保常量池中的数据的唯⼀

性,这就涉及到加锁机制,这些操作都是有需要占⽤CPU时间的,所以如果进⾏intern操作的是⼤量不会被重复利⽤的String的话,则有点

得不偿失。由此可见,()主要 适⽤于只有有限值,并且这些有限值会被重复利⽤的场景,如数据库表中的列名、⼈的姓⽒、编

码类型等。

6.总结:

()⽅法是⼀种⼿动将字符串加⼊常量池中的⽅法,原理如下:如果在常量池中存在与调⽤intern()⽅法的字符串等值的字符

串,就直接返回常量池中相应字符串的引⽤,否则在常量池中复制⼀份该字符串,并将其引⽤返回(Jdk7中会直接在常量池中保存当前字符

串的引⽤);Jdk6 中常量池位于PremGen区,⼤⼩受限,不建议使⽤()⽅法,不过Jdk7 将常量池移到了Java堆区,⼤⼩可控,

可以重新考虑使⽤()⽅法,但是由对⽐测试可知,使⽤该⽅法的耗时不容忽视,所以需要慎重考虑该⽅法的使⽤;()

⽅法主要适⽤于程序中需要保存有限个会被反复使⽤的值的场景,这样可以减少内存消耗,同时在进⾏⽐较操作时减少时耗,提⾼程序性

能。


本文发布于:2023-04-22 19:22:21,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/509734.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:jdk6
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图