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=桶个数
  • 考虑将字符串对象是否入池

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

入池前

在这里插入图片描述

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

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

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

相关文章

【Spring】(一)Spring设计核心思想

文章目录 一、初识 Spring1.1 什么是 Spring1.2 什么是 容器1.3 什么是 IoC 二、对 IoC 的深入理解2.1 传统程序开发方式存在的问题2.2 控制反转式程序的开发2.3 对比总结 三、对 Spring IoC 的理解四、DI 的概念4.1 什么是 DI4.2 DI 与 IoC的关系 一、初识 Spring 1.1 什么是…

Mermaid系列之FlowChart流程图

一.欢迎来到我的酒馆 介绍mermaid下&#xff0c;Flowchat流程图语法。 目录 一.欢迎来到我的酒馆二.什么是mermiad工具三.在vs code中使用mermaid四.基本语法 二.什么是mermiad工具 2.1 mermaid可以让你使用代码来创建图表和可视化效果。mermaid是一款基于javascript语言的图表…

宝塔面板点击SSL闪退打不开怎么解决?

宝塔Linux面板点击SSL证书闪退如何解决&#xff1f;旧版本的宝塔Linux面板确实存在这种情况&#xff0c;如何解决&#xff1f;升级你的宝塔Linux面板即可。新手站长分享宝塔面板SSL闪退的解决方法&#xff1a; 宝塔面板点击SSL证书闪退解决方法 问题&#xff1a;宝塔Linux面板…

谈谈网络安全

目录 1.概念 2.发展现状 3.主要问题 1.概念 网络安全是指保护计算机网络和其中的数据免受未经授权访问、损坏、窃取或破坏的过程和技术。网络安全涉及预防和检测潜在的威胁和漏洞&#xff0c;并采取措施保护网络的机密性、完整性和可用性。 网络安全的概念包括以下几个方面&am…

vue2 todoapp案例(静态)

1.创建三个子组件(TodoHeader、TodoMain、TodoFooter)和两个(index.css、base.css)样式&#xff1b; TodoHeader页面 <template><header class"header"><h1>todos</h1><input id"toggle-all" class"toggle-all" typ…

【力扣】链表题目总结

文章目录 链表基础题型一、单链表翻转、反转、旋转1.反转链表2.反转链表II——反转部分链表3.旋转链表4.K个一组翻转链表5.反转偶数长度组的节点 二、删除单链表中的结点1.删除链表的结点2.删除未排序链表中的重复节点3.删除已排序链表中的重复元素I——重复元素只剩下一个4.删…

【ASP.NET MVC】第一个登录页面(8)

一、准备工作 先从网上&#xff08;站长之家、模板之家&#xff0c;甚至TB&#xff09;下载一个HTML模板&#xff0c;要求一整套的CSS和必要的JS&#xff0c;比如下图&#xff1a; 登录页面的效果是&#xff1a; 首页&#xff1a; 利用这些模板可以减少前台网页的设计——拿来…

vue+element中如何设置单个el-date-picker开始时间和结束时间关联

功能&#xff1a;选了开始时间&#xff0c;则结束时间只能选择开始时间之后的&#xff1b;选了结束时间&#xff0c;则开始时间只能选择结束时间之前的 重点是picker-options属性 图示&#xff1a; 代码展示: // body 内部<el-form-item><el-date-pickerv-model&qu…

Qt+联想电脑管家

1.自定义按钮类 效果&#xff1a; (1)仅当未选中&#xff0c;未悬浮时 (2)其他三种情况&#xff0c;均如图 #ifndef BTN_H #define BTN_H#include <QPushButton> class btn : public QPushButton {Q_OBJECT public:btn(QWidget * parent nullptr);void set_normal_icon(…

02_kafka_基本概念_基础架构

文章目录 常见的消息队列工作模式基本概念kafka 特性Kafka 基本架构topic 分区的 目的/ 好处 日志存储形式消费者&#xff0c;消费方式 逻辑消费组 高性能写入&#xff1a; 顺序写 mmap读取&#xff1a;零拷贝DMA 使用场景 常见的消息队列工作模式 至多一次&#xff1a;消息被…

pytorch实战-图像分类(二)(模型训练及验证)(基于迁移学习(理解+代码))

目录 1.迁移学习概念 2.数据预处理 3.训练模型&#xff08;基于迁移学习&#xff09; 3.1选择网络&#xff0c;这里用resnet 3.2如果用GPU训练&#xff0c;需要加入以下代码 3.3卷积层冻结模块 3.4加载resnet152模 3.5解释initialize_model函数 3.6迁移学习网络搭建 3.…

UE4/5 GAS技能系统入门2 - AttributeSet

在GAS系统中对属性进行修改需要用到GE&#xff08;Gameplay Effect&#xff09;&#xff0c;而这又涉及到AttributeSet这样的概念。 AttributeSet用于描述角色的属性集合&#xff0c;如攻击力、血量、防御力等&#xff0c;与GAS系统整合度较高&#xff0c;本文就来讲一讲Attri…

Consul屏蔽api

consul 没有设置密码 需要屏蔽api&#xff1a;/v1/internal/ui/nodes?dc&token 防止信息泄露 配置config.json {"http_config": {"block_endpoints": ["/v1/internal/ui/nodes"]} }启动consul时使用该配置&#xff1a; consul agent -de…

Java实现Google cloud storage 文件上传,Google oss

storage 控制台位置 创建一个bucket 点进bucket里面&#xff0c;权限配置里&#xff0c;公开访问&#xff0c;在互联网上公开&#xff0c;需要配置角色权限 新增一个访问权限 &#xff0c;账号这里可以模糊搜索&#xff0c; 角色配置 给allUser配置俩角色就可以出现 在互联…

0.CLIP

目录 前言背景缘起/摘要数据集拟解决问题 精读IntroductionModel2.1自然语言监督2.2 创建一个有效的大数据集选择一个有效的预训练方法2.4 选择模型&#xff08;选择Encoder&#xff09;2.5训练小结 实验 复现&#xff08;略&#xff09; 前言 本课程来自深度之眼《多模态》训…

R语言3_安装SeurateData

环境Ubuntu22/20, R4.1 在命令行中键入&#xff0c; apt-get update apt install libcurl4-openssl-dev libssl-dev libxml2-dev libcairo2-dev libgtk-3-dev # libcairo2-dev :: systemfonts # libgtk :: textshaping进入r语言交互环境&#xff0c;键入&#xff0c; instal…

Mac显示隐藏文件夹

1、设置隐藏文件可见 defaults write com.apple.finder AppleShowAllFiles TRUE 2、killall Finder killall Finder

QtWebApp开发https服务器,完成客户端与服务器基于ssl的双向认证,纯代码操作

引言&#xff1a;所谓http协议&#xff0c;本质上也是基于TCP/IP上服务器与客户端请求和应答的标准&#xff0c;web开发中常用的http server有apache和nginx。Qt程序作为http client可以使用QNetworkAccessManager很方便的进行http相关的操作。Qt本身并没有http server相关的库…

深度学习——全维度动态卷积ODConv

ODConv(OMNI-DIMENSIONAL DYNAMIC CONVOLUTION)是一种关注了空域、输入通道、输出通道等维度上的动态性的卷积方法&#xff0c;因此被称为全维度动态卷积。 part1. 什么是动态卷积 动态卷积就是对卷积核进行线性加权 第一篇提出动态卷积的文章也是在SE之后&#xff0c;他提出…

快速排序【Java算法】

文章目录 1. 概念2. 思路3. 代码实现 1. 概念 快速排序是一种比较高效的排序算法&#xff0c;采用 “分而治之” 的思想&#xff0c;通过多次比较和交换来实现排序&#xff0c;在一趟排序中把将要排序的数据分成两个独立的部分&#xff0c;对这两部分进行排序使得其中一部分所有…