Day337&338.StringTable -JVM

Posted 阿昌喜欢吃黄桃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day337&338.StringTable -JVM相关的知识,希望对你有一定的参考价值。

StringTable

一、String基本特性

字符串常量池不放重复字符串

1、基本特性

2、String在jdk9中存储结构变更

为什么在jdk9中将String的底层value存放的形式变为byte[]字节数组

3、String存储结构变更


二、String内存分配

  • 存放流程:↓

  • 图示:↓


三、String基本操作

常量池不放重复字符串

  • 下面代码执行流程

左侧栈帧,右侧堆空间


四、字符串拼接操作

1、常量与常量拼接结构在常量池;通过编译器优化

2、只要其中有一个是变量,结果就在堆中;通过StringBuilder

3、如果声明的变量被final修饰

如果变量被final修饰,就会直接去引用字符串常量池里面的常量;

字符串拼接操作不一定使用的都是StringBuilder!

如果拼接符号左右两边都是字符串常量(String a = "XXX";)常量引用(被final修饰),仍然使用编译器优化

针对于final修饰类、方法、基本数据类型、引用数据类型的量结构时,能使用上final的时候建议就使用上。

4、拼接操作与使用append()效率对比

使用字符串拼接意味着,每次循环都要创建一次 StringBuilder、String对象

五、intern()的使用

1、API介绍


2、如何保证变量s指向的是字符串常量池中的数据?

3、面试题

4、JDK6 VS JDK 78的intern()总结

new String(“a”)就是在堆空间里创建一个空间和在字符串常量池中创建一个a,把堆空间中的引用给s变量;

s.intern()会判断s变量的a,是否在字符串常量池存在,这里的情况是已经存在了,所以没做什么;他也没有用变量接收;

因此,String s2 = “a”;这里变量s2的引用就会去直接引用在字符串常量池中已存在的a

因此比较变量s1和s2的地址值,必然是不一样的,s指向的是堆空间的a,而s2指向的是字符串常量池中的a,所以返回false

JDK6:↓↓

里面s3的操作这里,有创建4个对象:①StringBuilder②堆空间a③字符串常量池a④堆空间StringBuilder的append()结束后的new 的String(“aa”)对象,但他不会创建在字符串常量池中的aa对象;

此时,字符串常量池中有a,但没有aa,堆空间有StringBuilder、a、aa;

s3就相当于new了个aa,在堆中创建aa,但没有在字符串常量池中创建aa;

下面又执行了s3.intern(),就是判断字符串常量池中是否有aa,这里的情况是没有,所以就创建aa在字符串常量池;

接下来的s4就显式的指定了"aa",那肯定是字符串常量池中的aa的地址;

所以最后判断s3 与s4的地址 是false,一个是堆空间aa,一个是字符串常量池的aa

在JDK6,a.intern()会判断字符串常量池中是否有,如果没有就把a对象堆空间的内容复制一份,并放入字符串常量池

JDK7/8:↓↓

在JDK7/8中其他的一开始的部分是一样的,也是在给s3赋给堆空间aa,但没有创建字符串常量池的aa;

但接下来执行的s3.intern()方法,他在判断,判断字符串常量池中是否有aa,这里的情况肯定是没有的;

但是!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

他这里有做了改变,他确实在字符串常量池中创建了aa对象,但是!!但是!!;

他这里字符串常量池里aa对象的引用地址就直接引用了堆空间的aa的地址,为了节省空间

所以接下来s4 = “aa”,他给他赋上字符串常量池的aa地址,但是字符串常量池中的aa地址又指向了堆空间aa的地址,所以最终 s3的地址是堆空间aa的地址,s4的地址是字符串常量池aa的地址,但是字符串常量池aa的地址是堆空间aa的地址;

所以 s3 == s4;true

在JDK7/8,a.intern()会判断字符串常量池中是否有,如果没有就把a对象的堆空间的引用复制一份,并放入字符串常量池

  • 总结

  • 练习1

JDK6:↓

String s = new String(“a”)+new String(“b”);相当于就是String s = new String(“ab”)但是他没有在字符串常量池里面生成ab,当在堆空间创建了ab;

那么String s2 = s.intern(); 就会在判断在字符串常量池中判断是否有ab了没,结果是没有,所以s2就是赋值堆空间的内容,在字符串常量池创建ab的地址;

所以s2的地址是字符串常量池ab的地址,所以 s2 == “ab”,为true;

s ==“ab”,这里的"ab"是字符串常量池的ab,但是s的堆空间ab的地址,所以为false;

JDK78:↓

这里在JDK78的情况如上类似;

String s = new String(“a”)+new String(“b”);相当于就是String s = new String(“ab”)但是他没有在字符串常量池里面生成ab,当在堆空间创建了ab;

那么String s2 = s.intern(); 就会在判断在字符串常量池中判断是否有ab了没,结果是没有,所以s2就是赋值堆空间ab的引用,赋值给字符串常量池ab的地址;

所以s2的地址就是字符串常量池ab的地址,字符串常量池ab的地址是堆空间ab的地址;

所以s2 ==“ab”,s2是字符串常量池的地址,他s2引用的地址是字符串常量池的地址,字符串常量池的地址引用的堆空间ab的地址;为true;

而s ==“ab”,s是堆空间ab的地址,"ab"是字符串常量池的地址,但是!!"ab"字符串常量池的地址引用的是堆空间"ab"的地址,所以 为true;

  • 练习2:↓

String x = “ab”;会在字符串常量池中创建"ab";

String s = new String(“a”)+new String(“b”);等于 String s = new String(“ab”),但是没有在字符串常量池中创建ab,而在堆空间创建ab,所以s是堆空间ab的地址;

String s2 = s.intern(),会先判断字符串常量池中是否有ab,显然我们之前就已经在字符串常量池中创建了ab,所以s2的地址是字符串常量池ab;

s2 == x,s2是字符串常量池ab的地址,x为字符串常量池ab的地址,所以为true;

s = x,s是堆空间ab的地址,x为字符串字符串常量池ab的地址,所以为true;

这里不存在jdk6与78的区别,因为一开始x =ab;这里就已经在字符串常量池中放入了ab,就没有区别了

5、intern()效率测试:空间角度

在操作后是否调用intern(),来影响空间问题


六、StringTable的垃圾回收


七、G1中的String去重操作

字符串常量池里面本身不可能有重复的数据

1、背景

2、实现

3、命令行选项

以上是关于Day337&338.StringTable -JVM的主要内容,如果未能解决你的问题,请参考以下文章

<LeetCode OJ> 337. House Robber III

LeetCode 337 & 329. memorization DFSHouse Robber III

LeetCode 337

nodejs http代理请求

STM32F337:SPI从机帧同步

337_House Robber III