jvm常量池和字符串常量池_常量池中的字符串是对象吗

jvm常量池和字符串常量池_常量池中的字符串是对象吗JVM——字符串常量池详解引言在Java开发中不管是前后端交互的JSON串,还是数据库中的数据存储,我们常常需要使用到String类型的字符串。作为最常用也是最基础的引用数据类型,JVM为String提供了字符串常量池来提高性能,本篇文章我们一起从底层JVM中认识并学习字符串常量池的概念和设计原理。字符串常量池由来在日常开发过程中,字符串的创建是比较频繁的,而字符串的分配和其他对象的分配是类似的,需要耗费大量的时间和空间,从而影响程序的运行性能,所以作为最基础最常用的引用数据类型,Java设计者在

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

JVM——字符串常量池详解

请添加图片描述

引言

在Java开发中不管是前后端交互的JSON串,还是数据库中的数据存储,我们常常需要使用到String类型的字符串。作为最常用也是最基础的引用数据类型,JVM为String提供了字符串常量池来提高性能,本篇文章我们一起从底层JVM中认识并学习字符串常量池的概念和设计原理。

字符串常量池由来

在日常开发过程中,字符串的创建是比较频繁的,而字符串的分配和其他对象的分配是类似的,需要耗费大量的时间和空间,从而影响程序的运行性能,所以作为最基础最常用的引用数据类型,Java设计者在JVM层面提供了字符串常量池

实现前提

  1. 实现这种设计的一个很重要的因素是:String类型是不可变的,实例化后,不可变,就不会存在多个同样的字符串实例化后有数据冲突;
  2. 运行时,实例创建的全局字符串常量池中会有一张表,记录着长相持中每个唯一的字符串对象维护一个引用,当垃圾回收时,发现该字符串被引用时,就不会被回收

实现原理

为了提高性能并减少内存的开销,JVM在实例化字符串常量时进行了一系列的优化操作:

  1. 在JVM层面为字符串提供字符串常量池,可以理解为是一个缓存区;
  2. 创建字符串常量时,JVM会检查字符串常量池中是否存在这个字符串;
  3. 若字符串常量池中存在该字符串,则直接返回引用实例;若不存在,先实例化该字符串,并且,将该字符串放入字符串常量池中,以便于下次使用时,直接取用,达到缓存快速使用的效果。
	String str1 = "abc";
	String str2 = "abc";
	System.out.println("str1 == str2: " + (str1 == str2)); //结果:str1 == str2: true

字符串常量池位置变化

方法区

提到字符串常量池,还得先从方法区说起。方法区和Java堆一样(但是方法区是非堆),是各个线程共享的内存区域,是用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  很多人会把方法区称为永久代,其实本质上是不等价的,只不过HotSpot虚拟机设计团队是选择把GC分代收集扩展到了方法区,使用永久代来代替实现方法区。其实,在方法区中的垃圾收集行为还是比较少的,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,但是这个区域的回收总是不尽如人意的,如果该区域回收不完全就会出现内存泄露。当然,对于JDK1.8时,HostSpot VM对JVM模型进行了改造,将元数据放到本地内存常量池和静态变量放到了Java堆里。

元空间

JDK 1.8, HotSpot JVM将永久代移除了,使用本地内存来存储类的元数据信息,即为元空间(Metaspace)

所以,字符串常量池的具体位置是在哪里?当然这个我们后面需要区分jdk的版本,jdk1.7之前,jdk1.7,以及jdk1.8,因为这些版本中,字符串常量池因为方法区的改变而做了一些变化。

JDK1.7之前

在jdk1.7之前,常量池是存放在方法区中的。
在这里插入图片描述

JDK1.7

在jdk1.7中,字符串常量池移到了堆中,运行时常量池还在方法区中。
在这里插入图片描述

JDK1.8

jdk1.8删除了永久代,方法区这个概念还是保留的,但是方法区的实现变成了元空间,常量池沿用jdk1.7,还是放在了堆中。这样的效果就变成了:常量池和静态变量存储到了堆中类的元数据及运行时常量池存储到元空间中
在这里插入图片描述
为啥要把方法区从JVM内存(永久代)移到直接内存(元空间)?主要有两个原因:

  1. 直接内存属于本地系统的IO操作,具有更高的一个IO操作性能,而JVM的堆内存这种,如果有IO操作,也是先复制到直接内存,然后再去进行本地IO操作。经过了一系列的中间流程,性能就会差一些。非直接内存操作:本地IO操作——>直接内存操作——>非直接内存操作——>直接内存操作——>本地IO操作,而直接内存操作:本地IO操作——>直接内存操作——>本地IO操作
  2. 永久代有一个无法调整更改的JVM固定大小上限,回收不完全时,会出现OutOfMemoryError问题;而直接内存(元空间)是受到本地机器内存的限制,不会有这种问题。

变化

  1. 在JDK1.7前,运行时常量池+字符串常量池是存放在方法区中,HotSpot VM对方法区的实现称为永久代。
  2. 在JDK1.7中,字符串常量池从方法区移到堆中,运行时常量池保留在方法区中。
  3. 在JDK1.8中,HotSpot移除永久代,使用元空间代替,此时字符串常量池保留在堆中,运行时常量池保留在方法区中,只是实现不一样了,JVM内存变成了直接内存。

结合代码

字符串对象创建详解

代码示例

	String str1 = "123";
	String str2 = "123";
    String str3 = "123";
    String str4 = new String("123");
    String str5 = new String("123");
    String str6 = new String("123");

结果

str1 == str2:true
str2 == str3:true
str3 == str4:false
str4 == str5:false
str5 == str6:false

jvm存储示例

在这里插入图片描述

创建对象流程

对于jvm底层,String str = new String("123")创建对象流程是什么?

  1. 在常量池中查找是否存在”123″这个字符串;若有,则返回对应的引用实例;若无,则创建对应的实例对象;
  2. 在堆中new一个String类型的”123″字符串对象;
  3. 将对象地址复制给str,然后创建一个应用。

注意
若常量池里没有”123″字符串,则创建了2个对象;若有该字符串,则创建了一个对象及对应的引用。

Q&A

String str =“ab” + “cd”;对象个数?

分析:若字符串常量池该字符串对象

  1. 字符串常量池:(1个对象)“abcd”;
  2. 堆:无
  3. 栈:(1个引用)str
    总共:1个对象+1个引用

String str = new String(“abc”);对象个数?

分析:若字符串常量池没有该字符串对象

  1. 字符串常量池:(1个对象)“abc”;
  2. 堆:(1个对象)new String(“abc”)
  3. 栈:(1个引用)str
    总共:2个对象+1个引用

String str = new String(“a” + “b”);对象个数?

分析:若字符串常量池该字符串对象

  1. 字符串常量池:(3个对象)“a”,“b”,“ab”;
  2. 堆:(1个对象)new String(“ab”)
  3. 栈:(1个引用)str
    总共:4个对象+1个引用

String str = new String(“ab”) + “ab”;对象个数?

分析:若字符串常量池该字符串对象

  1. 字符串常量池:(1个对象)“ab”;
  2. 堆:(1个对象)new String(“ab”)
  3. 栈:(1个引用)str
    总共:2个对象+1个引用

String str = new String(“ab”) + new String(“ab”);对象个数?

分析:若字符串常量池该字符串对象

  1. 字符串常量池:(1个对象)“ab”;
  2. 堆:(2个对象)new String(“ab”),new String(“ab”)
  3. 栈:(1个引用)str
    总共:3个对象+1个引用

String str = new String(“ab”) + new String(“cd”);对象个数?

分析:若字符串常量池该字符串对象

  1. 字符串常量池:(2个对象)“ab”,“cd”;
  2. 堆:(2个对象)new String(“ab”),new String(“cd”)
  3. 栈:(1个引用)str
    总共:4个对象+1个引用

String str3 = str1 + str2;对象个数?

String str1 = "ab";
String str2 = "cd";
String str3 = str1 + str2;

分析:若字符串常量池该字符串对象

  1. 字符串常量池:(2个对象)“ab”,“cd”,“abcd”;
  2. 堆:无
  3. 栈:(3个引用)str1,str2,str3
    总共:2个对象+3个引用

如何指向字符串池中特定的对象?

通过intern()方法。
代码

        String str1 = "123";
        String str2 = new String("123");
        String str3 = str2;
        System.out.println("str1 == str2:" + (str1 == str2));
        System.out.println("str1 == str3:" + (str1 == str3));

		//通过java.lang.String.intern()方法指定字符串对象
        String str4 = str2.intern();
        System.out.println("str1 == str4:" + (str1 == str4));

结果

str1 == str2:false
str1 == str3:false
str1 == str4:true
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/164775.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • JavaScript 数组排序

    JavaScript 数组排序JavaScript数组排序1、reverse方法2、sort方法1、reverse方法reverse方法会将数组内的元素反序排序。如:letarr=[1,2,3,4,5,6];arr.reverse();//arr=[6,5,4,3,2,1]2、sort方法sort方法默认会将元素当成字符串相互对比,也可以传入自己写的比较函数来决定排序顺序。如:letarr=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19

    2022年6月14日
    32
  • 一文搞懂MySQL索引(清晰明了)[通俗易懂]

    一文搞懂MySQL索引(清晰明了)[通俗易懂]索引是对数据库表中一列或多列的值进行排序的一种结构。MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。MySQL中常用的索引结构(索引底层的数据结构)有:B-TREE,B+TREE,HASH等。MySQL的索引有两种分类方式:逻辑分类和物理分类。对于InnoDB和MyISAM而言,主键索引是根据主关键字来构建的B+树存储结构,辅助索引则是根据辅助键来构造的B+树存储结构,彼此的索引树都是相互独立的。

    2022年6月24日
    28
  • C++this指针

    C++this指针1)以下说法不正确的是:(括号内为个人理解) A.this指针就是指向成员函数所作用的对象的指针 B.每个对象的空间中都存放着一个this指针 C.类的非静态成员函数,真实的参数比所写的参数多1(多一个this指针) D.静态成员函数中不能使用this指针(因为static函数不属于某个对象) this指针是类的一个自动生成…

    2022年5月17日
    57
  • mac虚拟机连接网络问题

    mac虚拟机连接网络问题(转载:http://www.jb51.net/os/Windows8/84234.html)win8中mac虚拟机网络连接先设置自己的网络为共享然后设置VMnet8的TCP4/IPv4为自动获取ip然后将虚拟就的网络适配器的网络连接改成NAT然后根据自己的DNS设置mac上的DNS服务器然后就ok了转载于:https://www.cnblogs.com/tony0571/p/5581703…

    2022年6月17日
    101
  • 漏洞扫描 渗透测试_什么是渗透

    漏洞扫描 渗透测试_什么是渗透渗透测试阶段信息收集完成后,需根据所收集的信息,扫描目标站点可能存在的漏洞,包括SQL注入漏洞、跨站脚本漏洞、文件上传漏洞、文件包含漏洞及命令执行漏洞等,然后通过这些已知的漏洞,寻找目标站点存在攻击的入口。那么今天我们就介绍几款常用的WEB应用漏洞扫描工具。一、AWVSAcunetixWebVulnerabilityScanner(简称AWVS)是一款知名的网络漏洞扫描工具,它通过网络爬虫测试你的网站安全,检测流行安全漏洞。在漏洞扫描实战过程中,一般会首选AWVS,因为这个能扫描出来的漏洞很多,而

    2025年11月4日
    4
  • 原则与执着:暴雪王牌总结游戏研发得失成败「建议收藏」

    原则与执着:暴雪王牌总结游戏研发得失成败「建议收藏」暴雪执行副总裁、diablo3设计师RobPardo在本年度GDC上详细讲述了暴雪在游戏设计理念的主要原则,总结暴雪过去十余年中的经验教训。  RobPardo在暴雪长期负责游戏设计方面的工作,具有极其丰富的成功经验。他曾是《星际争霸》、《星际争霸:母巢之战》、《魔兽争霸3:混乱之治》、《魔兽争霸3:冰封王座》、《魔兽世界》、《魔兽世界:燃烧远征》的主要设计者,2006年他以《魔兽世界》

    2022年5月7日
    79

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号