理解字符串常量池(JVM)

大纲

在这里插入图片描述

思考

  1. 如何查看字符串常量池(StringTable)?

    使用 jclasslib 插件打开字节码,选择 常量池 -> 显示所选 -> CONSTANT_String_info,左侧过滤后的内容即为字符串常量池

  2. 字符串常量池、方法区、永久代和元空间的关系?

    方法区是逻辑概念,永久代和元空间是方法区的实现。字符串常量池总是在堆上

    • JDK7之前,方法区的实现是永久代,永久代是堆的一部分,而字符串常量池是永久代的一部分。
    • JDK7时,方法区的实现是永久代,永久代是堆的一部分,字符串常量池也是堆的一部分,字符串常量池和永久代并列。
    • JDK8及之后,方法区的实现是元空间,元空间是操作系统直接内存,字符串常量池还是堆的一部分。
  3. Java中什么是常量?static final 修饰的量就是常量吗?

    • 从字节码层面上看,字段有 ConstantValue 修饰的就是常量(包含对常量池的索引),如下图所示
      在这里插入图片描述
    • 从 Java 源代码层面上看
      • static final 修饰的 int、double 等基本数据类型(不包含 Integer 等包装类)
      • static final 修饰的 String
      • 字面量,例如 1 或者 "abcd"

    注意事项:static final String s = "abc" 中的 s 是常量,但 static final String s = new String("abc") 中的 s 并不是常量,同样 static final Integer num = 1 中的 num 也不是常量。

  4. 字符串拼接问题

    • 只有常量的情况,会存在编译优化,只会将最后生成的字符串添加到字符串常量池,例如 String s = "ab" + "cd" 字符串常量池中只会出现 "abcd"
    • 出现变量的情况,会创建 StringBuilder 对象,通过 append() 来追加字符内容,最终调用 toString() 来创建字符串对象。

测试 intern() 的作用

发现一个 bug,下面的代码如何批量测试时,internTest03() 方法的结果是 false、true。单独测试该方法时,返回结果是 true、true。

public class InternTest {@Testpublic void internTest01() {String s = new StringBuilder().append('a').append('b').append('c').append('d').toString();String intern = s.intern();System.out.println(intern == s);//true}@Testpublic void internTest02() {String s0 = "abcd";String s = new StringBuilder().append('a').append('b').append('c').append('d').toString();String intern = s.intern();System.out.println(intern != s);//trueSystem.out.println(intern == s0);//true}@Testpublic void internTest03() {String s = new StringBuilder().append('a').append('b').append('c').append('d').toString();String intern = s.intern();String s0 = "abcd";System.out.println(intern == s);//trueSystem.out.println(intern == s0);//true}}

测试创建多少个对象的代码


public class HowManyObjectTest {@Testpublic void howManyObjectTest01() {String s = "abcd";}@Testpublic void howManyObjectTest02() {String s = "ab" + "cd";}@Testpublic void howManyObjectTest03() {String s = new String("abcd");}@Testpublic void howManyObjectTest04() {String s = new String("ab" + "cd");// 验证字符串常量池中是否存在某个字符串的方法:// 还可以通过jclasslib直接查看字符串常量池中是否出现"ab"// String ab = new String("a") + new String("b");// String intern = ab.intern();// System.out.println(ab == intern);  //true,证明"ab"是通过ab.intern()放入字符串常量池的,之前并不存在。}@Testpublic void howManyObjectTest05() {String s = new String("abcd") + "abcd";}static String s1 = "aaa";static String s2 = "bbb";@Testpublic void howManyObjectTest06() {String s = s1 + s2;}static final String s3 = "aaaa";static final String s4 = "bbbb";@Testpublic void howManyObjectTest07() {String s = s3 + s4;}static final String s5 = new String("aaaaa");static final String s6 = new String("bbbbb");@Testpublic void howManyObjectTest08() {String s = s5 + s6;}
}

问题一:new String("abcd") 会创建多少个对象?

 0 new #3 <java/lang/String>3 dup4 ldc #2 <abcd>6 invokespecial #4 <java/lang/String.<init> : (Ljava/lang/String;)V>9 astore_1
10 return

上面是 new String("abcd") 的字节码,下面对上面的内容进行解释:

  1. new #3:表示创建一个 java.lang.String 类型的对象(分配对象空间),并将地址压入操作数栈。
    (其中 #3 表示字符串常量池中的索引,此时指向 java.lang.String

    在这里插入图片描述

  2. dup:对操作数栈的栈顶元素进行复制,并压入操作数栈

    在这里插入图片描述

  3. ldc #2:从常量池中加载字符串 “abcd” 对象,并压入操作数栈

    在这里插入图片描述

  4. invokespecial #4:调用 String 类初始化方法 init<>,由于调用含参构造,因此消耗操作数栈中的两个参数
    (从更通用的角度来看,这个 init<> 的逻辑包含 (a)显示赋值、(b)代码块赋值、(c)构造器方法调用

    在这里插入图片描述

  5. astore_1:将初始化完成的 String 对象的引用赋值给局部变量 s

    在这里插入图片描述

问题二:new String("abcd") + "abcd" 会创建多少个对象?

 0 new #5 <java/lang/StringBuilder>3 dup4 invokespecial #6 <java/lang/StringBuilder.<init> : ()V>7 new #3 <java/lang/String>
10 dup
11 ldc #2 <abcd>
13 invokespecial #4 <java/lang/String.<init> : (Ljava/lang/String;)V>
16 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
19 ldc #2 <abcd>
21 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
24 invokevirtual #8 <java/lang/StringBuilder.toString : ()Ljava/lang/String;>
27 astore_1
28 return
  1. new $5dupinvokespecial #6:创建一个 StringBuilder 对象,并初始化

    在这里插入图片描述

  2. new #3dupldc #2invokespecial #4new String("abcd") 的代码逻辑

    在这里插入图片描述

  3. invokevirtual #7:调用 append() 方法,返回值类型还是 StringBuilder,消耗操作数栈中的两个元素,并返回一个元素

    注意:如果此时发生GC,那么 0x100 对应的 String 对象会被标记为垃圾。因为从 GCRoot 出发不可达。疑问:字符串常量池是否会进行垃圾回收?其中的对象在什么情况下会进行垃圾回收?

    在这里插入图片描述

  4. ldc #2:直接压入字符串常量池中地址
    在这里插入图片描述

  5. invokevirtual #7:再次调用 append() 方法

    在这里插入图片描述

  6. invokespecial #8:调用 toString() 方法,创建 String 对象

    在这里插入图片描述

  7. astore_1:赋值给局部变量 s
    在这里插入图片描述

其它问题

  1. 验证 String s = "ab" + "cd" 执行完成后,字符串常量池中并不存在 "ab""cd"
  2. 使用 StringBuilder 对象调用 append() 逐个追加字符,最终得到的字符串不会放入到字符串常量池中
  3. 使用 char[] 来创建 String 对象,得到的字符串不会放入到字符串常量池中,StringBuilder 本质上维护了一个动态扩容的 char[]
  4. static final Integer num = 1static final String s = new String("abcd") 中的 num 和 s 均不是常量(可以通过 clinit<> 的代码逻辑进行查看)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/760.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何将你的iOS应用成功上架App Store(图文详解)

上架基本需求资料 1、苹果开发者账号&#xff08;如还没账号先申请- 苹果开发者账号申请教程&#xff09; 2、开发好的APP 通过本篇教程&#xff0c;可以学习到ios证书申请和打包ipa上传到appstoreconnect.apple.com进行TestFlight测试然后提交审核的完整流程&#xff01; …

草稿 | word格式的网址索引

参考文献引用 参考文献上标设置&#xff1a;&#xff08;改为上标的快捷键为ctrlshift“”&#xff09; https://jingyan.baidu.com/article/cbcede07d786c743f50b4d47.html 多个参考文献一起引用&#xff1a; https://blog.csdn.net/neptune4751/article/details/119921187 交…

社交媒体数据恢复:与你科技

在数字时代&#xff0c;数据是我们生活中的重要组成部分。无论是个人照片、文档&#xff0c;还是企业的重要资料&#xff0c;数据在我们的生活中扮演着举足轻重的角色。然而&#xff0c;数据丢失的问题时常发生&#xff0c;给我们带来了很多麻烦。幸运的是&#xff0c;当下众多…

UE4 拍摄、保存并浏览相册

效果&#xff1a; 1.新建CameraActor类 2.修改截图保存路径 3.编写BP_Camera蓝图 注意路径 Save Image函数要在执行拍照和BeginPlay事件执行一次 按钮执行拍摄事件 3.编写UMG蓝图 技巧&#xff1a;让Index加1、减1循环赋值 4.把BP_Camera挂在玩家上

步步精科技获得发明型专利,提升Type-C连接器行业竞争力

在电子科技日新月异的时代&#xff0c;连接器作为电子设备中不可或缺的一部分&#xff0c;其安全性、稳定性和性能水平直接关系到设备的使用效果和用户体验。深圳市步步精科技有限公司&#xff08;以下简称“步步精科技”&#xff09;一直致力于连接器领域的技术创新和产品研发…

Ubuntu上阅读Android源码工具

由于Android源码过于庞杂&#xff0c;里面有多种语言源文件&#xff0c;想只用一IDE统一索引是不现实的。我个人便使用AS阅读JAVA代码&#xff0c;VS看C/C代码&#xff0c;在Ubuntu上不能使用SI&#xff0c;所以直接放弃。在framework开发这个层面上来讲&#xff0c;因为大部分…

vue中使用水印

1. 在utils下创建watermark.js const watermark {}/**** param {要设置的水印的内容} str* param {需要设置水印的容器} container* param {需要设置水印的每一块的宽度} canWidth* param {需要设置水印的每一块的高度} canHeight* param {需要设置水印的字体} canFont* para…

Qt - 窗口

目录 1. 前言 2. 菜单栏(QMenuBar) 2.1. 创建菜单栏 2.1.1. 方式一 2.1.2. 方式二 2.2. 在菜单栏中添加菜单和创建菜单项 2.3. 在菜单项之间添加分割线 2.4. 综合示例 3. 工具栏(QToolBar) 3.1. 创建工具栏 3.2. 设置停靠位置 3.2.1. 方式一 3.2.2. 方式二 3.3. 设…

桥接模式【结构型模式C++】

1.概述 桥接模式是一种结构型设计模式&#xff0c;是用于把抽象化与实现化解耦&#xff0c;使得二者可以独立变化。这种类型的设计模式属于结构型模式&#xff0c;它通过提供抽象化和实现化之间的桥接结构&#xff0c;来实现二者的解耦。 这种模式涉及到一个作为桥接的接口&am…

go语言并发实战——日志收集系统(四) 利用tail包实现对日志文件的实时监控

Linux中的tail命令 tail 命令是一个在 Unix/Linux 操作系统上用来显示文件末尾内容的命令。它可以显示文件的最后几行内容&#xff0c;默认情况下显示文件的最后 10 行。tail 命令 非常有用&#xff0c;特别是在我们查看日志文件或者监视文件变化时。 基本用法如下&#xff1a…

Flume在大数据集群下的配置以及监控工具Ganglia的部署安装

前提&#xff1a;需要有三台虚拟机&#xff08;hadoop102,103,104&#xff09;配置好相关基础环境 安装 将安装包上传到/opt/software中 tar -zxf /opt/software/apache-flume-1.9.0-bin.tar.gz -C /opt/module/修改 apache-flume-1.9.0-bin 的名称为 flume mv /opt/module/…

element table加减列

// 有个特别注意的地方,下面这行代码,key一定绑的是item,千万不要绑定index,不然就会出现异常 //<el-table-column v-for"(item,index) in titleList" :key"item" min-width"150" align"center"><el-table fit :data"d…

从智能家居到智能城市:物联网中的隐私和安全风险

随着科技的不断进步&#xff0c;智能设备和物联网&#xff08;IoT&#xff09;技术已经逐渐渗透到我们的生活中。从智能家居设备到智能城市的实现&#xff0c;这些设备和技术可以让我们的生活变得更加便捷和高效。但是&#xff0c;这些设备也带来了不可忽视的隐私和安全风险。 …

【QT进阶】Qt Web混合编程之html、 js的简单交互

往期回顾 【QT进阶】Qt Web混合编程之VS2019 CEF的编译与使用&#xff08;图文并茂超详细介绍&#xff09;-CSDN博客【QT进阶】Qt Web混合编程之QWebEngineView基本用法-CSDN博客【QT进阶】Qt Web混合编程之CMake VS2019编译并使用QCefView&#xff08;图文并茂超详细版本&…

奇怪的 NRST 管脚异常复位问题

1. 引言 本文探讨一个奇怪的 MCU NRST 管脚异常复位现象。 2. 复位问题及排查 这个问题是客户对开发的平台做 EMS 浪涌测试的时候发生的&#xff0c; 平台上使用了一个STM32G474 RCT6 MCU 。在某个等级的 EMS 测试中&#xff0c; 客户发现 MCU 有时候会异常复位而影响平台的…

Linux驱动开发笔记(一)字符驱动

文章目录 前言一、字符设备驱动程序框架二、基本原理1. 设备号的申请与归还2. 保存file_operations接口3. 设备节点的创建和销毁4. 创建文件设备4.1 mknod4.2 init_special_incode( )函数 5. 查找file_operation接口函数速查表 三、程序编写1. 模块初始化及关闭2. 文件操作方式…

Pytorch 学习路程

目录 下载Pytorch 入门尝试 几种常见的Tensor Scalar Vector Matrix AutoGrad机制 线性回归尝试 使用hub模块 Pytorch是重要的人工智能深度学习框架。既然已经点进来&#xff0c;我们就详细的介绍一下啥是Pytorch PyTorch 希望将其代替 Numpy 来利用 GPUs 的威力&…

Python --- 在python中安装NumPy,SciPy,Matplotlib以及scikit-learn(Windows平台)

在python中安装NumPy&#xff0c;SciPy&#xff0c;Matplotlib以及scikit-learn(Windows平台) 本文是针对(像我一样的)python新用户所写的&#xff0c;刚刚在电脑上装好python之后&#xff0c;所需的一些常见/常用的python第三方库/软件包的快速安装指引。包括了这些常用安装包…

(mac)性能监控平台搭建JMeter+Grafana+Influxdb

【实现原理】 通过influxdb数据库存储jmeter的结果&#xff0c;再通过grafana采集influxdb数据库数据&#xff0c;完成监控平台展示 一、时间序列数据InfluxDB 1.InfluxDB下载安装 官网下载 https://portal.influxdata.com/downloads/ 官网最新版&#xff1a; &#xff0…

测试用例的编写评审

1、什么叫软件测试用例 什么是测试用例 测试用例(TestCase) 是为项目需求而编制的一组测试输入、执行条件 以及预期结果&#xff0c;以便测试某个程序是否满足客户需求。–测试依据 可以总结为:每一个测试点的数据设计和步骤设计。–测试用例 2、测试用例的重要性(了解) 2.1…