JVM-深入学习字符串常量池

2022-09-08 13:53:53 浏览数 (1)

JVM-深入学习字符串常量池

第一二张图应该画错了,元空间的时候,字符串常量池已经移入堆内

首先声明,在JDK1.7的时候,字符串常量池已经从方法区迁移到了堆内存,JDK1.8的时候方法区改朝换代为元空间,同时也不在占用JVM内存,而是使用本地内存

为什么多设计一个常量池,不能像其它对象一样乖乖待在堆中吗?鄙人大胆猜测:

  • 对象的分配需要时间和空间的开销,一般在程序中。字符串使用的频段还是很高的,另一方面,常用的数据库密码、用户名啥的都是使用字符串保存的,如果不将其放入字符串常量池的话,使其频繁创建销毁的话,性能和安全性都是划不来的,所以这就是为什么会有字符串常量池和String为什么是final修饰的原因。

上代码,下面这种字面量声明的方式是我们最常用的方式。这样声明会直接将字符串”晓果冻“放入字符串常量池

再声明一个对象s2,因为“晓果冻”已经在字符串常量池中存在了,所以s2直接指向字符串常量池中的地址

代码语言:javascript复制
String s2 = "晓果冻";

new String("")呢,分2种情况

  1. new的字符串已经在常量池存在,那么堆就会指向字符串常量池中的地址

​ 为什么会是false呢,因为s1引用的是字符串常量池中的地址,s2引用的是堆内地址,但堆内对象是引用了字符串地址的,如果 s2 = s2.intern();

其实这里也不能算第二种情况,就是new好了再调用上面的**intern()**方法,又是另一种情形;

intern():不管使用什么方式定义一个字符串,都会首先在常量池中查找是否有相应的字符串存在,如果有,直接返回引用,否则,在常量池中生成相应的字符串并返回引用;

下面上代码

代码语言:javascript复制
package com.company;

public class Main {

    public static void main(String[] args) {
	// write your code here
        String s1 = "晓果冻";
        String s2 = "晓果冻";
        String s3 = "晓"   "果冻";
        String s4 = new String("晓");
        String s5 = new String("果冻");
        String s6 = s4   "果冻";
        String s7 = s4   s5;
        String s8 = s6.intern();
        String s9 = s7.intern();
        System.out.println(s1 == s2);
        System.out.println(s1 == s3);
        System.out.println(s1 == s6);
        System.out.println(s6 == s7);
        System.out.println(s6 == s8);
        System.out.println(s8 == s9);
        System.out.printf(s7);
    }
}

号操作是有内部优化处理操作的,其实也是又new了一个新的字符串对象,所以上图字符串常量池中的"晓"和"果冻"字符串没有被引用。因为这种 操作这种图我不知道该怎么画,所以只能指向最终的堆中地址。

代码语言:javascript复制
s6.intern()如果不把返回值赋值给s6,那么栈内存中的对象s6还是引用堆中的地址。
只有s6=s6.intern();//这样栈内存中的对象s6才会引用常量池中的地址,故s1==s6
代码语言:javascript复制
String s10 = new String("晓果冻");

/如果字符串常量池中没有"晓果冻"字符串的话,那么直接在堆中创建该字符串,并不会复制一份到字符串常量池的,大多数人都会以为会复制一份到字符串常量池,其实不然。 只有当s10.intern();在常量池新增了一个对象,但是并没有将字符串复制一份到常量池,而是直接指向了之前已经存在于堆中的字符串对象。因为在 JDK 1.7 之后,字符串常量池不一定就是存字符串对象的,还有可能存储的是一个指向堆中地址的引用

0 人点赞