java8中字符串常量以及GC相应处理机制
1,常量池
1.1, class文件常量池
class文件常量池位于class文件中
class文件头4个字节称为魔数,魔数后面的4个字节为文件版本号,而版本号之后的就是常量池的入口。该常量池用于存放编译器生成的各种字面量和符号引用,字面量就是所谓的常量,如字符串,final修饰的常量值等。而符号引用则是用来描述引用目标的,如类和接口的全限定名,方法和字段的名称和描述符。此时符号引用并不会存储最终内存布局信息。
class文件中方法和字段均需要引用CONSTANT_Utf8型常量描述名称,该类型的最大长度也就是java中方法和字段名的长度,而该类型的长度用2个字节表示,最大值为65535,所以java中如果定义了超过64kb(2^16*1byte,utf8中一个字符占一个字节)的英文字符的变量或方法名,将无法编译。
1.2, 运行时常量池
当类或接口创建时,它的class中的常量池会被用来构造运行时常量池,常量池中的符号引用会被解析成具体的内存地址。运行时常量池是jvm方法区的一部分,它可以在运行时将符号引用解析为直接引用。
运行时常量池位于jvm的元空间中(java8)
1.3,字符串常量池
字符串常量池底层实现是一个哈希表,可以通过-XX:StringTableSize参数调整大小。字符串常量池中存储的是字符串对象的引用,而字符串本身是在堆上分配的(java中的对象基本都在堆上分配)。运行时常量池初始化的时候,字面量的符号引用的初始化会用到字符串常量池。String中的intern方法可以在运行时将字符串实例加入字符串常量池。
在java1.7以前,字符串常量池是在堆的永久代里面,大小固定,而从java1.7以后,字符串常量池则移动到java堆中了。
验证代码如下:
public class test {
private static String str = "";
public static void main(String[] args) {
//-Xms32m -Xmx32m
char element = 'a';
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 1024*1024*64; i++) {
sb.append(element);
}
str = sb.toString();
System.out.println();
}
}
JVM启动参数:-Xms32m -Xmx32m
运行结果如下:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
......
at test.test.main(test.java:17)
可以发现堆发生溢出(oom),说明常量池在堆中分配。
String.intern()
String类的intern()方法的描述是:String类维护着一个初始化为空的字符串池,当某String实例调用intern()方法的时候,如果字符串池中已包含与此字符串相同的字符串(用equal(obj)方法判断相等),则返回池中的字符串对象。否则,将此字符串对象加入到常量池中,并返回此字符串对象的引用。对于任意两个字符串s和t,当切仅当s.equals(t)为true,s.intern() ==t.intern()才为true。所有字面值字符串和字符串赋值常量表达式都使用intern方法进行处理。
示例:
public class InternTest {
public static void main(String[] args) {
String s1 = new String("a");
s1.intern();
String s2 = "a";
System.out.println(s1 == s2);
String s3 = new String("b") + new String("b");
s3.intern();
String s4 = "bb";
System.out.println(s3 == s4);
}
}
运行结果如下:
false
true
-XX:StringTableSize
可以通过jvm参数-XX:StringTableSize来配置String常量池的大小,在java6和java7u40之前的版本,其默认大小为1009,java7u40以及之后的java8中其默认值为60013。其值要求得是素数。
2,使用new关键字和使用字符串字面量的区别
示例:
String str1="abc";
String str2=new String("abc");
当使用
String str="abc"这种形式的时候,JVM会在字符串常量池中创建该对象并且str1会指向这个对象;当使用
String str=new String("abc")这种形式的时候,JVM会先检查在字符串常量池中是否已存在"abc",如果不存在则在常量池中创建该对象,如果已存在则不创建。除此之外,还会在堆外创建一个额外的字符串对象,并且str2会指向这个对象。
验证代码:
public class test {
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1);
System.out.println(str2);
System.out.println(str1 == str2);
}
}
运行结果如下:
abc
abc
false
查看该代码的字节码,会发现两种创建字符串对象的方式的不同:
L0
LINENUMBER 9 L0
LDC "abc" //从常量池加载String
ASTORE 1
L1
LINENUMBER 10 L1
NEW java/lang/String //创建一个String对象
DUP
LDC "abc" //从常量池加载String
INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V //调用构造方法,实例初始化
ASTORE 2
通过对比发现,使用new关键字与直接使用字符串字面量相比,多了创建对象的过程。
3,字符串常量池与GC
先看一个示例:
public class test {
public static void main(String[] args) {
System.out.println("---------String with new--------");
collectWeakReference(new String("dasfsafsafsafsa"));
System.out.println("---------String with literal--------");
collectWeakReference("dsafdsafxcdfeghg");
}
private static void collectWeakReference(String obj ) {
ReferenceQueue<String> rq = new ReferenceQueue<>();
Reference<String> r = new WeakReference<>(obj, rq);
obj = null;
Reference rf;
int gccount = 10;
//一次System.gc()并不一定会回收A,所以要多试几次
while((rf=rq.poll()) == null && gccount >=0) {
System.gc();
gccount--;
}
System.out.println(rf);
if (rf != null) {//如果对象被回收则弱引用会加入引用队列
//引用指向的对象已经被回收,存入引入队列的是弱引用本身,所以这里最终返回null
System.out.println(rf.get());
}
}
}
运行结果如下:
---------String with new--------
java.lang.ref.WeakReference@5a07e868
null
---------String with literal--------
null
由此可见,指向new关键字创建的字符串对象的弱引用会被System.gc()触发的gc回收掉,而指向字面量字符串的弱引用则不会被System.gc()触发的gc回收掉。这说明new关键字创建的字符串对象如果不可达了会被gc回收,而字符串字面量创建的字符串对象则不会,因为常量池中还持有对该字面量字符串的引用。
参考链接:
https://netsurfingzone.com/core-java/string-constant-pool-in-java
https://tangxman.github.io/2015/07/27/the-difference-of-java-string-pool/
https://blog.csdn.net/zm13007310400/article/details/77534349
https://stackoverflow.com/questions/23252767/string-pool-vs-constant-pool
http://java-performance.info/string-intern-in-java-6-7-8/
https://netsurfingzone.com/core-java/string-constant-pool-in-java
https://www.cnblogs.com/gxyandwmm/p/9495923.html
https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html
https://blog.csdn.net/qq_26929957/article/details/79090406
https://jimmy2angel.github.io/2018/10/26/ConstantPool/
https://droidyue.com/blog/2014/12/21/string-literal-pool-in-java/
https://blog.csdn.net/qq_31615049/article/details/81611918
https://blog.omitol.com/2017/09/30/JVM-CP/
java8中字符串常量以及GC相应处理机制的更多相关文章
- 《挑战30天C++入门极限》C/C++中字符串常量的不相等性及字符串的Copy
C/C++中字符串常量的不相等性及字符串的Copy #include <iostream> void main(void) { if("test&quo ...
- C语言中字符串常量到底存在哪了?
常量存储总结局部变量.静态局部变量.全局变量.全局静态变量.字符串常量以及动态申请的内存区 1.局部变量存储在栈中2.全局变量.静态变量(全局和局部静态变量)存储在静态存储区3.new申请的内存是在堆 ...
- 浅谈JAVA中字符串常量的储存位置
在讲述这些之前我们需要一些预备知识: java中的内存被分成以下部分: 1.栈区:由编译器自动分配释放,具体方法执行结束后,系统自动释放JVM内存资源. 其作用有保存局部变量的值,包括:1.用来保存基 ...
- 收藏:C语言中字符串常量到底存在哪了?
来源:https://www.cnblogs.com/jaysir/p/5041799.html 常量存储总结局部变量.静态局部变量.全局变量.全局静态变量.字符串常量以及动态申请的内存区 1.局部变 ...
- Java中字符串的几个实例
String str=new String("abc");new 对象时,位于堆中,同时看字符串常量中是否有字符串"abc",如果没有,则进行添加,同时进行关联 ...
- Java String类相关知识梳理(含字符串常量池(String Pool)知识)
目录 1. String类是什么 1.1 定义 1.2 类结构 1.3 所在的包 2. String类的底层数据结构 3. 关于 intern() 方法(重点) 3.1 作用 3.2 字符串常量池(S ...
- 一文带你认识Java8中接口的默认方法
Java8是Oracle于2014年3月发布的一个重要版本,其API在现存的接口上引入了非常多的新方法. 例如,Java8的List接口新增了sort方法.在Java8之前,则每个实现了List接口的 ...
- Java中的字符串常量池
ava中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new ...
- 转载:Java中的字符串常量池详细介绍
引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...
随机推荐
- (三十一)c#Winform自定义控件-文本框(四)
前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...
- 如何将自己的代码发布到Maven中央仓库?
去年在公司做工作流相关业务时,当时使用flowable做引擎,中途涉及到一些业务上的需求,自己整理了一些代码,考虑到开源精神,当时就想着将于公司业务无关的代码抽离出来,放到Maven中央仓库中,以供别 ...
- Java基础及JavaWEB以及SSM框架学习笔记Xmind版
Java基础及JavaWEB以及SSM框架学习笔记Xmind版 转行做程序员也1年多了,最近开始整理以前学习过程中记录的笔记,以及一些容易犯错的内容.现在分享给网友们.笔记共三部分. JavaSE 目 ...
- Okhttp3源码解析(2)-Request分析
### 前言 前面我们讲了 [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析]( ...
- c语言的图形库
图形库链接http://www.easyx.cn/ 使用图形库头文件easyx.h或graphics.h 同样在里面下载图形库帮助文档进行查询 vs vc都可使用图形库 图形库窗口: initgrap ...
- 驰骋工作流引擎与jFinal集成版本2.0
驰骋工作流引擎与jFinal集成版本2.0 发布说明 关键字: 驰骋工作流程快速开发平台 工作流程管理系统java工作流引擎. 使用协议:GPL. 关于JFinal: https://www.jfin ...
- MQTT服务器(Win)
系统环境准备 Java JDK >=1.6,系统环境变量配置JAVA HOME 链接:https://pan.baidu.com/s/1OO-KCdsCrdfjMtf6BVNl6Q 提取码:dy ...
- 常用Linux备份
用于备份的Tar 备份工具Tar是以前备份文件的可靠方法,几乎可以工作于任何环境中,Linux老用户一般都信赖它. Linux中以.tar结尾的文件都是用tar创建的.它的使用超出了单纯的备份,可用来 ...
- Python机器学习笔记:不得不了解的机器学习知识点(2)
之前一篇笔记: Python机器学习笔记:不得不了解的机器学习知识点(1) 1,什么样的资料集不适合用深度学习? 数据集太小,数据样本不足时,深度学习相对其它机器学习算法,没有明显优势. 数据集没有局 ...
- Jenkins教程——从安装到部署Docker服务(二)声明式流水线HelloWorld
前言 本文通过一个声明式流水线的HelloWorld程序做一下流水线基础入门,对常用的流水线参数进行简要说明 什么是流水线 现实中的流水线 流水线比较好理解,类比于现实生活中的生产流水线,每个流程只做 ...