理解字符串常量池(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 {
    @Test
    public 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
    }


    @Test
    public 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);//true
        System.out.println(intern == s0);//true
    }


    @Test
    public 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);//true
        System.out.println(intern == s0);//true
    }

}

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


public class HowManyObjectTest {


    @Test
    public void howManyObjectTest01() {
        String s = "abcd";
    }

    @Test
    public void howManyObjectTest02() {
        String s = "ab" + "cd";
    }


    @Test
    public void howManyObjectTest03() {
        String s = new String("abcd");
    }

    @Test
    public 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()放入字符串常量池的,之前并不存在。
    }

    @Test
    public void howManyObjectTest05() {
        String s = new String("abcd") + "abcd";
    }


    static String s1 = "aaa";
    static String s2 = "bbb";

    @Test
    public void howManyObjectTest06() {
        String s = s1 + s2;
    }

    static final String s3 = "aaaa";
    static final String s4 = "bbbb";

    @Test
    public void howManyObjectTest07() {
        String s = s3 + s4;
    }


    static final String s5 = new String("aaaaa");
    static final String s6 = new String("bbbbb");

    @Test
    public void howManyObjectTest08() {
        String s = s5 + s6;
    }
}

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

 0 new #3 <java/lang/String>
 3 dup
 4 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 dup
 4 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.mfbz.cn/a/559119.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Dynamic Wallpaper for Mac:动态壁纸让桌面更生动

Dynamic Wallpaper for Mac是一款为苹果电脑用户精心设计的动态壁纸软件&#xff0c;它以其丰富的功能和精美的壁纸库&#xff0c;为用户带来了更加生动和个性化的桌面体验。 Dynamic Wallpaper for Mac v17.8中文版下载 这款软件支持多种动态壁纸&#xff0c;用户可以根据自己…

unity学习(86)——细节优化

东西已经做出来了&#xff0c;现在需要的是优化&#xff0c;说得简单&#xff0c;做起来难。 1.122包的优化&#xff0c;避免重复创建&#xff01; 2.为何会出现一边动&#xff0c;一边不动的情况。重复登录后依旧是unity可以看到移动&#xff0c;但是exe那边看不到移动&#…

数据结构PT1——线性表/链表

1&#xff1a;顺序存储实现(数组实现) Data&#xff1a; a1 a2 .....ai ai1 .... an .... typedef struct LNode *List; //指向LNode的指针&#xff0c;这是typedef的&#xff0c;你可以随时声明&#xff0c;而不加typedef只是创建一个 struct LNode{ //结构体成员ElementT…

Vue.js------Vue组件基础

能够理解Vue组件概念和作用能够掌握封装创建组件能力能够使用组件之间通信能够完成todo案例 一.Vue组件创建和使用 1.折叠面板-实现多个 创建一个文件夹demo 具体步骤请参考vue.js---vue基础 ⚫ 解决方案: 采用vue提供的单.vue文件-组件方式来封装一套然后复用 在component…

Jackson 2.x 系列【24】Spring Web 集成

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 前言2. Spring Web3. Jackson2ObjectMapperBuilder4. Jackson2ObjectMapperFa…

探索传感器世界:类型与应用详解

传感器是一种能感知并测量特定物理量、化学量或其他参数&#xff0c;并将其转换为可供处理、记录或控制的电信号的装置。 物联网传感器设备种类繁多&#xff0c;以下是一些常见的类型&#xff1a; 一、 温度传感器 1、热电阻温度传感器&#xff1a;利用金属的电阻随温度变化的…

Java编程题 | 数组元素交换

大家可以关注一下专栏&#xff0c;方便大家需要的时候直接查找&#xff0c;专栏将持续更新~ 题目描述 编写一个Java程序&#xff0c;输入一个整数数组&#xff0c;将最大的元素与第一个元素交换&#xff0c;最小的元素与最后一个元素交换&#xff0c;然后输出修改后的数组…

PHP定时任务框架taskPHP3.0学习记录4宝塔面板bash定时任务(轮询指定json文件字段后确定是否执行、环境部署、执行日志、文件权限)

一 需求说明 宝塔面板中,读取指定 /www/wwwroot/lockdata/cron/webapp.json文件&#xff1b;配置定时任务脚本task.sh&#xff1b;当读取webapp.json中&#xff0c;如果cron_task1&#xff0c;则执行任务php start.php start命令行&#xff1b;完成命令后&#xff0c;执行cron…

Vue3基本功能介绍

文章目录 Vue3组件中的模板结构可以没有根标签div组合式APIRefReactive函数回顾Vue2响应式Vue3实现响应式对比reactive和refSetup注意点计算属性与监听computedWatchWatchEffectVue3生命周期自定义hook函数toRef其他组合APIshallowReactiveshallowRefreadonly和shallowOnlyToRa…

PostgreSql-Install

PostgreSql源码安装 一、源代码下载二、操作系统配置三、编译安装四、启动数据库五、相关命令 PostgreSQL是一个强大的 开源对象关系数据库系统&#xff0c;它使用并扩展了SQL语言&#xff0c;并结合了许多功能&#xff0c;可以安全地存储和扩展最复杂的数据工作负载。 一、源…

Targeted influence maximization in competitive social networks

abstract 利用口碑效应的广告对于推销产品是相当有效的。在过去的十年中&#xff0c;人们对营销中的影响力最大化问题进行了深入的研究。影响力最大化问题旨在将社交网络中的一小群人识别为种子&#xff0c;最终他们将引发网络中最大的影响力传播或产品采用。在网络营销的实际场…

HTML、CSS常用的vscode插件 +Css reset 和Normalize.css

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 ✍HTML、CSS常用的vscode插件&#x1f34e;1 HTML 标签同步重命名 – Auto Re…

大型网站系统架构演化实例_7.使用NoSQL和搜索引擎

1.使用NoSQL和搜索引擎 随着网站业务越来越复杂&#xff0c;对数据存储和检索的需求也越来越复杂&#xff0c;网站需要采用一些非关系数据库技术如NoSQL和非数据库查询技术如搜索引擎。NoSQL和搜索引擎都是源自互联网的技术手段&#xff0c;对可伸缩的分布式特性具有更好的支持…

【代理模式】静态代理-简单例子

在Java中&#xff0c;静态代理是一种设计模式&#xff0c;它涉及到为一个对象提供一个代理以控制对这个对象的访问。静态代理在编译时就已经确定&#xff0c;代理类和被代理类会实现相同的接口或者是代理类继承被代理类。客户端通过代理类来访问&#xff08;调用&#xff09;被…

QT跨平台读写Excel

QT跨平台读写Excel 背景Excel工具CMakeLists.txt工程目录 背景 开发框架QT&#xff0c;makefile构建工具CMake&#xff0c;编译器MinGW Excel工具 考虑跨平台则不能使用针对微软COM组件的QAxObject来读写Excel&#xff0c;因此使用开源QtXlsx。 这里是将QXlsx当做源码嵌入使…

【Linux学习】Linux权限(二)

文章目录 &#x1f680;Linux权限管理&#x1f680;修改文件的所有者&#x1f680;修改文件或目录的所属组&#x1f680;同时修改为念的拥有者与所属组&#x1f680;文件类型&#x1f680;file指令&#x1f680;目录权限&#x1f680;umask指令&#x1f680;粘滞位 &#x1f68…

使用 Docker 部署 instantbox 轻量级 Linux 系统

1&#xff09;instantbox 介绍 GitHub&#xff1a;https://github.com/instantbox/instantbox instantbox 是一款非常实用的项目&#xff0c;它能够让你在几秒内启动一个主流的 Linux 系统&#xff0c;随起随用&#xff0c;支持 Ubuntu&#xff0c;CentOS&#xff0c; Arch Li…

c#+unity基础

序列化&#xff1a; [SerializeField]&#xff0c;点不出来&#xff0c;只能在面板上显示绑定游戏物体 //公有隐藏 特有函数 特有函数&#xff1a;不需要调用&#xff0c;自动执行 Awake最先执行->OnEable 面向对象思想 面向对象思想&#xff1a;分为具体对象和抽象对…

nas如何异地共享文件?

nas异地共享文件是一种通过网络实现不同地区电脑与电脑、设备与设备、电脑与设备之间的文件共享的技术。通过nas&#xff08;网络附加存储&#xff09;设备&#xff0c;用户可以在不同地点的电脑或设备之间快速、安全地共享文件和数据。本文将介绍nas异地共享文件的原理以及它在…

day4网络编程作业

#include <myhead.h> #define SER_IP "192.168.125.78" #define SER_PORT 69 #define CLI_IP "192.168.125.176" #define CLI_PORT 4399 //文件上传 void upload(int cfd,struct sockaddr_in sin)//服务器信息结构体传参 {//填充读写请求字符数组--&…
最新文章