常量池之字符串常量池String.intern()

运行时常量池是方法区(PermGen)的一部分。

需要提前了解: 1. JVM内存模型2. JAVA对象在JVM中内存分配

常量池的好处

常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。

字符串进入到常量池的两种方法:

1. new String()的实例调用intern()方法。     执行intern()方法时,若常量池中不存在等值的字符串,JVM就会在常量池中 创建一个等值的字符串,然后返回该字符串的引用。 2. “”(引号)引起来的内容(字面量)。     引号引起来的字符串,首先从常量池中查找是否存在此字符串,如果不存在则在常量池中添加此字符串对象,然后引用此字符串对象。如果存在,则直接引用此字符串。

重要提示:虚拟机启动时常量池中就存在“java”字符串实例,下面代码中s2调用intern()方法时,只是返回常量池中“java”实例的引用,而没有添加“java”实例。

先看下,下面的代码

public class Test {
    public static void main(String[] args) {
        String s1 = new StringBuilder().append("aa").append("bb").toString();
        System.out.println(s1.intern() == s1);
        String s2 = new StringBuilder().append("ja").append("va").toString();
        System.out.println(s2.intern() == s2); 
    }
}

输出结果如下:

JDK6

false false

JDK7、JDK8

true false

为什么不同版本的JDK输出的结果还不一样呢?带着疑问我们分别分析下JDK1.6 和JDK1.7-JDK1.8的常量池内存模型。

JDK1.6 常量池内存模型

Paste_Image.png

如图中,我们可以看到常量池位于方法区(PermGen),常量池中引用的字符串实例也在常量池中。

  1. 通过调用intern()方法,会在常量池中生成一个相同字符串的对象
  2. “”内的字符串都会添加到常量池中,相当于引用的方法区中的字符串对象。

根据上图内存模型然后我们在分析下代码:

String s1 = new StringBuilder().append("aa").append("bb").toString();     System.out.println(s1.intern() == s1);     s1生成的对象在堆中,而s1.intern()的对象在常量池中,所以返回false。

String s2 = new StringBuilder().append("ja").append("va").toString();     System.out.println(s2.intern() == s2);     s2生成的对象在堆中,而s2.intern()的对象也肯定在常量池中,所以也返回false。

JDK1.7-JDK1.8常量池内存模型

Paste_Image.png

如图中,我们可以看到常量池位于方法区(PermGen),但常量池引用的字符串实例在中。(区别在这)

  1. 通过调用intern()方法,会在常量池添加一个此字符串实例的引用,(前提:常量池中没有相同内容的字符串)。
  2. “”内的字符串实例引用会添加到常量池中(前提:常量池中没有相同内容的字符串),如果常量池中存在,则引用常量池中的对象(防止重复创建对象)。

根据上图内存模型然后我们在分析下代码:     String s1 = new StringBuilder().append("aa").append("bb").toString();     System.out.println(s1.intern() == s1);     s1生成的对象在堆中,此时常量池中没有跟s1内容相同的字符串,所以在调用intern方法时,会在常量池中添加此对象的引用,所以返回为true。

String s2 = new StringBuilder().append("ja").append("va").toString();     System.out.println(s2.intern() == s2);     s2生成的对象在堆中,而此时常量池中已经有一个跟s2内容相同的字符串常量,当s2调用intern方法时,返回常量池中已经存在的实例(相当于堆中有两个相同内容的实例:一个是new 出来的,一个是常量池中的)所以返回的结果为false。

“java”字符串实例为什么会在虚拟机启动时就存在?

请看下面的例子

 public void meme(){
    String s = new StringBuilder().append("me").append("me").toString();
    System.out.println(s.intern()==s);   //false
}

执行结果为false,说明“meme”也已经存在常量池中了。     常量池定义:用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。     当类加载到内存后会在常量池中添加类、方法等符号的引用。方法名meme就会以字符串的方式加入到字符串常量池中,所以会感觉常量池中会内置很多字符串常量。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菜鸟致敬

Python中is和==的区别

Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)。

10730
来自专栏smy

php unset对json_encode的影响

发现对一个数组unset前后,变量的类型变化了,unset前是数组,unset后是对象

9410
来自专栏大前端_Web

深入解析js中基本数据类型与引用类型,函数参数传递的区别

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/articl...

15040
来自专栏技术博文

2.js中对于函数参数不确定的解决--arguments

1.arguments对象 在函数代码中,使用特殊对象 arguments,无需明确指出参数名,就能访问它们。 例如,在函数 sayHello() 中,第一个参...

28540
来自专栏柠檬先生

JavaScript 基础(五) 函数 变量和作用域

函数定义和调用    定义函数,在JavaScript中,定义函数的方式如下:       function abs(x){         if...

19990
来自专栏海天一树

小朋友学Python(10):C/C++/Java/Python的关键字

一、C语言关键字 C语言关键字有32个 autobreakcasecharconstcontinuedefaultdodoubleelseenumexternf...

28080
来自专栏我爱编程

Day16正则表达式

正则表达式(Regular Expression)的主要功能是从字符串(string)中通过特定的模式,搜索希望找到的内容。比如想找到小说中的所有人名,找到字符...

30180
来自专栏lhyt前端之路

js的this、call、apply、bind、继承、原型链0.前言1.this2.call、apply、bind3.从call到继承

这些都是js基础进阶的必备了,有时候可能一下子想不起来是什么,时不时就回头看看基础,增强硬实力。

10410
来自专栏PHP实战技术

这10个问题你一定要会!你肯定忽略了!

1、问题一关于弱类型 $str1 = 'yabadabadoo'; $str2 = 'yaba'; if (strpos($str1,$str2)) { ec...

34560
来自专栏开发与安全

从零开始学C++之对象的使用(二):四种对象生存期和作用域、static 用法总结

一、四种对象生存期和作用域 ? 栈对象 隐含调用构造函数(程序中没有显式调用) 堆对象 隐含调用构造函数(程序中没有显式调用),要显式释放 全局...

20700

扫码关注云+社区

领取腾讯云代金券