当前位置: 首页 > news >正文

JVM基础篇-StringTable

StringTable

特性

  • 常量池中的字符串仅是符号,第一次用到时才变为对象

  • 利用串池的机制,来避免重复创建字符串对象

  • 字符串变量拼接的原理是 StringBuilder (1.8)

  • 字符串常量拼接的原理是编译期优化

  • 可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池

    • 1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回
    • 1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池, 会把串池中的对象返回
场景1
package com.vmware.jvm;public class Demo1 {public static void main(String[] args) {String a="a";String b="b";String c="ab";}
}
  • 在JVM中会预先准备好一块内存空间 StringTable [] ,其内容为空 数据结构采用HashTable
  • 此时符号a、b、ab都还是常量池中的符号,还没有变为java字符串对象
  • ldc #2会将a符号变为a字符串对象,然后判断StringTable中是否存在字符串a,如果不存在则添加到StringTable中,否则直接使用StringTable中的串
  • ldc #3会将b符号变为b字符串对象 然后判断StringTable中是否存在字符串b,如果不存在则添加到StringTable中,否则直接使用StringTable中的串
  • ldc #4会将ab符号变为ab字符串对象 然后判断StringTable中是否存在字符串ab,如果不存在则添加到StringTable中,否则直接使用StringTable中的串
  • 执行结束后: StringTable [“a”,“b”,“ab”]

❗️ 注意:字符串不会预先加载到StringTable中,而是当使用该字符串时才会被加载,其行为为懒惰型

反编译代码

      stack=1, locals=4, args_size=10: ldc           #2                  // String a  2: astore_1                                       3: ldc           #3                  // String b  5: astore_2                                       6: ldc           #4                  // String ab 8: astore_3                                       9: return
场景2
public class Demo1 {public static void main(String[] args) {String a = "a";String b = "b";String c = "ab";String d = a + b;}
}

反编译如下

9: new           #5                  // class java/lang/StringBuilder //new StringBuilder()
12: dup
13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V 调用无参构造方法
16: aload_1                           //从LocalVariableTable中加载1号槽的变量"a"
17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; //调用StringBuild().append("a")
20: aload_2                           //从LocalVariableTable中加载2号槽的变量"b"
21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;//调用StringBuild().append("b")
24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
//调用toString()方法  -> new String("ab");
27: astore        4                   //存储到LocalVariableTable4号槽

StringBuild toString方法源码

 	@Overridepublic String toString() {// Create a copy, don't share the arrayreturn new String(value, 0, count);}

相当于

new StringBuild().append("a").append("b").toString();

调用new String(“ab”)后相当于在堆中创建了一个新的字符串对象,所以

d==c  //false d在堆中 c在StringTable中
编译期优化

javac会认为e的结果是确定的,所以进行了编译期优化

String e = "a" + "b";

反编译

 29: ldc           #4                  // String ab
  • 相当于直接从常量池中加载符号ab
c==e //true  c第一次被加载到StringTable中,e与c相同不会再次进入StringTable,e指向的地址与c的地址相同
字符串延迟加载证明

在这里插入图片描述

  • 可以使用IDEA开启debug模式后,使用内存检测功能,观察字符串的数量
package com.vmware.jvm;public class Demo2 {public static void main(String[] args) {System.out.println();//2433System.out.println("a");//2434System.out.println("b");//2235System.out.println("c");//2436System.out.println("a");//2436System.out.println("b");//2436System.out.println("c");//2436}
}
  • 代码每执行一行,String的数量+1,说明字符串没有进行预加载
  • 第二次使用字符串时,String的数量没有增加
场景三
public class Demo3 {public static void main(String[] args) {String x="ab";String s = new String("a") + new String("b");//实际上利用StringBuild的append方法进行拼接,"a" "b"会被先加载到串池中 StringTable=["ab","a","b"]String s2 = s.intern();//入池失败   s2-> table."ab"  s-> heap."ab"  x-> table."ab"System.out.println(s == x);//falseSystem.out.println(s2 == x);//true}
}
  • intern方法在jdk1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,并返回串池中的对象
  • 在jdk1.6中将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池, 会把串池中的对象返回
String s2 = s.intern();//s2 -> table  s-> heap
  • intern方法注释:调用 intern 方法时,如果池已包含与equals(Object)方法比较后相等的String字符串,则返回池中的字符串。否则,此String对象将添加到池中,并返回对此String对象的引用
问题
package com.vmware.jvm;public class Demo4 {public static void main(String[] args) {String s1 = "a";String s2 = "b";String s3 = "a" + "b";String s4 = s1 + s2;String s5 = "ab";String s6 = s4.intern();//问System.out.println(s3 == s4);//false  s3在编译期进行优化 s3="ab" 在StringTable中  s4=new StringBuild().append("a").append("b"),toString() 在堆中System.out.println(s3 == s5);//true s3进入StringTable s5发现StringTable中有"ab"则指向Table中的地址System.out.println(s3 == s6);//true  s4调用intern方法后入池失败,返回池中的"ab"所以 s6即为池中的"ab"String x2 = new String("c") + new String("d");String x1 = "cd";x2.intern();//将对象new String("cd")放入池中,入池失败//问 如果调换了后两行的位置呢,如果是jdk1.6呢System.out.println(x1 == x2);//jdk1.8 false x2指向堆,x2调用入池方法后入池失败 x2指向堆 x1指向StringTable//交换位置  x2入池成功,x2指向池中的cd  x1指向池中的c 所以为true//jdk1.6 首先在堆中new String("cd") 然后调用intern方法,会对堆中的对象拷贝一份入池,而x2仍然指向堆中的对象  x1发现池中有"cd"直接使用池中的对象,所以为false}
}
String Table的位置

在这里插入图片描述

  • jdk1.6:永久代
  • jdk1.7、1.8:堆内存

🔖 为什么要将StringTable从永久代移动到堆中?

因为永久代的内存只有当触发FullGC的时候才会回收,而堆内存在触发MinorGC的时候就会触发回收,由于StringTable使用比较频繁,所以JVM工程师对位置进行了移动

垃圾回收
package com.vmware.jvm;/*** @apiNote StringTable垃圾回收延时* -Xmx10m:设置堆大小为10m* -XX:+PrintStringTableStatistics 打印StringTable信息* -XX:+PrintGCDetails -verbose:gc 打印垃圾回收信息** 初始信息* StringTable statistics:* Number of buckets       :     60013 =    480104 bytes, avg   8.000* Number of entries       :      1711 =     41064 bytes, avg  24.000  //StirngTable中的key-value对 1711* Number of literals      :      1711 =    154040 bytes, avg  90.029  //字符串数量1711* * 添加10000个字符串到StringTable后* StringTable statistics:* Number of buckets       :     60013 =    480104 bytes, avg   8.000* Number of entries       :      5845 =    140280 bytes, avg  24.000* Number of literals      :      5845 =    353352 bytes, avg  60.454* 数量小于11711,说明StringTable内进行了垃圾回收*/
public class Demo6 {public static void main(String[] args) {int i = 0;try {for (int j = 0; j < 10000; j++) {String.valueOf(j).intern();}} catch (Throwable e) {e.printStackTrace();} finally {System.out.println(i);}}
}
性能调优

StringTable采用的数据结构为哈希表,所以可以通过调整主数组的大小来减小哈希碰撞的概率,进而提升效率

  • 调整StringTable的桶大小
-XX:StringTableSize=桶个数
  • 考虑将字符串对象是否入池

如果程序运行期间会产生大量字符串,并且字符串重复数量较多,可以考虑将字符串进行入池操作,减少内存开销

入池前

在这里插入图片描述

入池后
在这里插入图片描述

相关文章:

JVM基础篇-StringTable

StringTable 特性 常量池中的字符串仅是符号&#xff0c;第一次用到时才变为对象 利用串池的机制&#xff0c;来避免重复创建字符串对象 字符串变量拼接的原理是 StringBuilder &#xff08;1.8&#xff09; 字符串常量拼接的原理是编译期优化 可以使用 intern 方法&#…...

探秘手机隐藏的望远镜功能:开启后,观察任何你想看的地方

当今的智能手机不仅仅是通信工具&#xff0c;它们蕴藏着各种隐藏的功能&#xff0c;其中之一就是让你拥有望远镜般的观察能力。是的&#xff0c;你没有听错&#xff01;今天我们将探秘手机中隐藏的望远镜功能&#xff0c;这项神奇的功能可以让你打开后&#xff0c;轻松观察任何…...

正运动亮相2023半导体设备材料与核心部件展示会,助力半导体产业高速高精应用

■展会名称&#xff1a; 第11届&#xff08;2023&#xff09;半导体设备材料与核心部件展示会 ■展会日期 2023年8月9日-11日 ■展馆地点 无锡太湖国际博览中心A6馆 ■展位号 A6-A361 正运动技术&#xff0c;作为国内领先的运动控制企业&#xff0c;将于2023年8月9日参加…...

如何在MongoDB中添加新用户

如何在MongoDB中添加新用户&#xff1f; MongoDB是一款流行的NoSQL数据库&#xff0c;它的可扩展性强&#xff0c;可进行分布式部署&#xff0c;且具有高可用性。其许多优势使得越来越多的企业和组织选择MongoDB作为其数据库系统。本文将介绍如何在MongoDB中添加新用户。 第一步…...

幻读怎么复现

大家好&#xff0c;我是想想。 很久没有给大家分享技术了&#xff0c;主要在计划一些事情&#xff0c;几乎没什么时间爽文了。 今天从实操上实现了MySQL事务隔离复现问题&#xff0c;就记录分享给大家吧。 正文 我们知道&#xff0c;著名的四大事务特性ACID特性 Atomicity…...

无脑入门pytorch系列(二)—— torch.mean

本系列教程适用于没有任何pytorch的同学&#xff08;简单的python语法还是要的&#xff09;&#xff0c;从代码的表层出发挖掘代码的深层含义&#xff0c;理解具体的意思和内涵。pytorch的很多函数看着非常简单&#xff0c;但是其中包含了很多内容&#xff0c;不了解其中的意思…...

ansible-kubeadm在线安装高可用K8S集群v1.19-v1.20版本

ansible可以安装的KS8版本如下&#xff1a; 请按照此博客中的内容操作后&#xff0c;才可以通过下面的命令查询到版本。 [rootk8s-master01 ~]# yum list kubectl --showduplicates | sort -r kubectl.x86_64 1.20.0-0 kubern…...

Cesium entity 渐隐渐显、闪烁

点entity function f2(){var x1;var flogtrue;//闪烁//var x0;var flogfalse;//渐显viewer.entities.add({name:"圆点point闪烁",position:Cesium.Cartesian3.fromDegrees(116.200.03,39.530.03,0),point : {show : true, // defaultcolor :new Cesium.CallbackProp…...

LISA:通过大语言模型进行推理分割

论文&#xff1a;https://arxiv.org/pdf/2308.00692 代码&#xff1a;GitHub - dvlab-research/LISA 摘要 尽管感知系统近年来取得了显著的进步&#xff0c;但在执行视觉识别任务之前&#xff0c;它们仍然依赖于明确的人类指令来识别目标物体或类别。这样的系统缺乏主动推理…...

opencv基础40-礼帽运算(原始图像减去其开运算)cv2.MORPH_TOPHAT

礼帽运算是用原始图像减去其开运算图像的操作。礼帽运算能够获取图像的噪声信息&#xff0c;或者得到比原始图像的边缘更亮的边缘信息。 例如&#xff0c;图 8-22 是一个礼帽运算示例&#xff0c;其中&#xff1a; 左图是原始图像。中间的图是开运算图像。右图是原始图像减开运…...

php中的array_filter()函数

php中的array_filter()函数用于筛选数组中的元素&#xff0c;并返回一个新的数组&#xff0c;新数组的元素是所有返回值为true的原数组元素。 array_filter()函数的使用语法如下&#xff1a; array_filter ( array $array [, callable $callback [, int $flag 0 ]] ) : array…...

ArcGIS Pro基础:【按顺序编号】工具实现属性字段的编号自动赋值

本次介绍一个字段的自动排序编号赋值工具&#xff0c;基于arcgis 的字段计算器工具也可以实现类似功能&#xff0c;但是需要自己写一段代码实现&#xff0c; 相对而言不是很方便。 如下所示&#xff0c;该工具就是【编辑】下的【属性】下的【按顺序编号】工具。 其操作方法是…...

neo4j终端操作

1】进入容器 (base) xiaokkkxiaokkkdeMacBook-Pro ~ % docker exec -it 77ed5fe2b52e /bin/bash 2】启动、停止neo4j root77ed5fe2b52e:/var/lib/neo4j/bin# ./neo4j start Neo4j is already running (pid:7). Run with --verbose for a more detailed error message.root7…...

【深度学习】在 MNIST实现自动编码器实践教程

一、说明 自动编码器是一种无监督学习的神经网络模型&#xff0c;主要用于降维或特征提取。常见的自动编码器包括基本的单层自动编码器、深度自动编码器、卷积自动编码器和变分自动编码器等。 其中&#xff0c;基本的单层自动编码器由一个编码器和一个解码器组成&#xff0c;编…...

SpringBoot3基础用法

技术和工具「!喜新厌旧」 一、背景 最近在一个轻量级的服务中&#xff0c;尝试了最新的技术和工具选型&#xff1b; 即SpringBoot3&#xff0c;JDK17&#xff0c;IDEA2023&#xff0c;Navicat16&#xff0c;虽然新的技术和工具都更加强大和高效&#xff0c;但是适应采坑的过程…...

6、移除链表元素

方法1&#xff1a;原链表删除元素 伪代码&#xff1a; 首先判断头节点是否是待删除元素。&#xff08;头节点和其他节点的删除方法不一样&#xff09; while(head ! null && head->value target) //如果链表为 1 1 1 1 1&#xff0c;要删除元素1时用if就会失效 {h…...

大厂容器云实践之路(一)

1-华为CCE容器云实践 华为企业云 | CCE容器引擎实践 ——从IaaS到PaaS到容器集群 容器部署时代的来临 IaaS服务如日中天 2014-2015年&#xff0c;大家都在安逸的使用IaaS服务&#xff1b; 亚马逊AWS的部署能力方面比所有竞争对手…...

《合成孔径雷达成像算法与实现》Figure3.1

代码复现如下&#xff1a; clc close all clear all%参数设置 B 5.80e6; %信号带宽 T 7.26e-6; %脉冲持续时间 K B/T; %线性调频频率 alpha 5; %过采样率 F alpha*B; %采样频率 N F*T; %采样点数 dt T/N; …...

代码随想录算法训练营day60

文章目录 Day60 柱状图中最大的矩形题目思路代码 Day60 柱状图中最大的矩形 84. 柱状图中最大的矩形 - 力扣&#xff08;LeetCode&#xff09; 题目 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图…...

Modbus TCP转Profibus DP网关modbus tcp报文解析

捷米JM-DPM-TCP网关。在Profibus总线侧作为主站&#xff0c;在以太网侧作为ModbusTcp服务器功能&#xff0c; 下面是介绍捷米JM-DPM-TCP主站网关组态工具的配置方法 2, Profibus主站组态工具安装 执行资料光盘中的安装文件setup64.exe或setup.exe安装组态工具。安装过程中一直…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...