java 获取常量池_Java 字符串常量池介绍

2023-10-30

本文将介绍 HotSpot 中的 String Pool,字符串常量池。相对是一篇比较简单的文章,大家花几分钟就看完了。

在 Java 世界中,构造一个 Java 对象是一个相对比较重的活,而且还需要垃圾回收,而缓存池就是为了缓解这个问题的。

我们来看下基础类型的包装类的缓存,Integer 默认缓存 -128 ~ 127 区间的值,Long 和 Short 也是缓存了这个区间的值,Byte 只能表示 -127 ~ 128 范围的值,全部缓存了,Character 缓存了 0 ~ 127 的值。Float 和 Double 没有缓存的意义。

Integer 可通过设置 java.lang.Integer.IntegerCache.high 扩大缓存区间

String 不是基础类型,但是它也有同样的机制,通过 String Pool 来缓存 String 对象。假设 “Java” 这个字符串我们会在应用程序中使用多次,我们肯定不希望在每次使用到的时候,都重新在堆中创建一个新的对象。

当然,之所以 Integer、Long、String 这些类的对象可以缓存,是因为它们是不可变类

基础类型包装类的缓存池使用一个数组进行缓存,而 String 类型,JVM 内部使用 HashTable 进行缓存,我们知道,HashTable 的结构是一个数组,数组中每个元素是一个链表。和我们平时使用的 HashTable 不同,JVM 内部的这个 HashTable 是不可以动态扩容的。

7a0252bec8606796bf2fe183d91e5cc5.png

创建和回收

当我们在程序中使用双引号来表示一个字符串时,这个字符串就会进入到 String Pool 中。当然,这里说的是已被加载到 JVM 中的类。

这是一个不够严谨的说法,请参见评论区的讨论。

另外,就是 String#intern() 方法,这个方法的作用就是:

如果字符串未在 Pool 中,那么就往 Pool 中增加一条记录,然后返回 Pool 中的引用。

如果已经在 Pool 中,直接返回 Pool 中的引用。

只要 String Pool 中的 String 对象对于 GC Roots 来说不可达,那么它们就是可以被回收的。

如果 Pool 中对象过多,可能导致 YGC 变长,因为 YGC 的时候,需要扫描 String Pool,可以看看笨神大佬的文章《JVM源码分析之String.intern()导致的YGC不断变长》。

讨论 String Pool 的实现

1、首先,我们先考虑 String Pool 的空间问题。

在 Java 6 中,String Pool 置于 PermGen Space 中,PermGen 有一个问题,那就是它是一个固定大小的区域,虽然我们可以通过 -XX:MaxPermSize=N 来设置永久代的空间大小,但是不管我们设置成多少,它终归是固定的。

所以,在 Java 6 中,我们应该尽量小心使用 String.intern() 方法,否则容易导致 OutOfMemoryError。

到了 Java 7,大佬们已经着手去掉 PermGen Space 了,首先,就是将 String Pool 移到了堆中。

把 String Pool 放到堆中,即使堆的大小也是固定的,但是这个时候,对于应用调优工作,只需要调整堆大小就行了。

到了 Java 8,PermGen 已经被彻底废弃,出现了堆外内存区域 MetaSpace,String Pool 相应的从堆转移到了 MetaSpace 中。

在 Java 8 中,String Pool 依然还是在 Heap Space 中。感谢评论区的读者指出错误。大家可以看一下我后面写的关于 MetaSpace 的文章,那篇文章深入分析了 MetaSpace 的构成。

2、其次,我们再讨论 String Pool 的实现问题。

前面我们说了 String Pool 使用一个 HashTable 来实现,这个 HashTable 不可以扩容,也就意味着极有可能出现单个 bucket 中的链表很长,导致性能降低。

在 Java 6 中,这个 HashTable 固定的 bucket 数量是 1009,后来添加了选项(-XX:StringTableSize=N)可以配置这个值。到 Java 7(7u40),大佬们提高了这个默认值到 60013,Java 8 依然也是使用这个值,对于绝大部分应用来说,这个值是足够用的。当然,如果你会在代码中大量使用 String#intern(),那么有必要手动设置一下这个值。

为什么是 1009,而不是 1000 或者 1024?因为 1009 是质数,有利于达到更好的散列。60013 同理。

JVM 内部的 HashTable 是不扩容的,但是不代表它不 rehash,它会在发现散列不均匀的时候进行 rehash,这里不展开介绍。

3、观察 String Pool 的使用情况。

JVM 提供了 -XX:+PrintStringTableStatistics 启动参数来帮助我们获取统计数据。

遗憾的是,只有在 JVM 退出的时候,JVM 才会将统计数据打印出来,JVM 没有提供接口给我们实时获取统计数据。

SymbolTable statistics:

Number of buckets : 20011 = 160088 bytes, avg 8.000

Number of entries : 10923 = 262152 bytes, avg 24.000

Number of literals : 10923 = 425192 bytes, avg 38.926

Total footprint : = 847432 bytes

Average bucket size : 0.546

Variance of bucket size : 0.545

Std. dev. of bucket size: 0.738

Maximum bucket size : 6

## 看下面这部分:

StringTable statistics:

Number of buckets : 60003 = 480024 bytes, avg 8.000

Number of entries : 4000774 = 96018576 bytes, avg 24.000

Number of literals : 4000774 = 1055252184 bytes, avg 263.762

Total footprint : = 1151750784 bytes

Average bucket size : 66.676

Variance of bucket size : 19.843

Std. dev. of bucket size: 4.455

Maximum bucket size : 84

统计数据中包含了 buckets 的数量,总的 String 对象的数量,占用的总空间,单个 bucket 的链表平均长度和最大长度等。

上面的数据是在 Java 8 的环境中打印出来的,Java 7 的信息稍微少一些,主要是没有 footprint 的数据:

StringTable statistics:

Number of buckets : 60003

Average bucket size : 67

Variance of bucket size : 20

Std. dev. of bucket size: 4

Maximum bucket size : 84

测试 String Pool 的性能

接下来,我们来跑个测试,测试下 String Pool 的性能问题,并讨论 -XX:StringTableSize=N 参数的作用。

我们将使用 String#intern() 往字符串常量池中添加 400万 个不同的长字符串。

package com.javadoop;

import java.lang.ref.WeakReference;

import java.util.ArrayList;

import java.util.List;

import java.util.WeakHashMap;

public class StringTest {

public static void main(String[] args) {

test(4000000);

}

private static void test(int cnt) {

final List lst = new ArrayList(1024);

long start = System.currentTimeMillis();

for (int i = 0; i < cnt; ++i) {

final String str = "Very very very very very very very very very very very very very very " +

"very long string: " + i;

lst.add(str.intern());

if (i % 200000 == 0) {

System.out.println(i + 200000 + "; time = " + (System.currentTimeMillis() - start) / 1000.0 + " sec");

start = System.currentTimeMillis();

}

}

System.out.println("Total length = " + lst.size());

}

}

我们每插入 20万 条数据,输出一次耗时。

# 编译

javac -d . StringTest.java

# 使用默认 table size (60013) 运行一次

java -Xms2g -Xmx2g com.javadoop.StringTest

# 设置 table size 为 400031,再运行一次

java -Xms2g -Xmx2g -XX:StringTableSize=400031 com.javadoop.StringTest

a6f65482cc447611dc02fdb7144dd2cb.png

从左右两部分数据可以很直观看出来,插入的性能主要取决于链表的平均长度。当链表平均长度为 10 的时候,我们看到性能是几乎没有任何损失的。

还是那句话,根据自己的实际情况,考虑是否要设置 -XX:StringTableSize=N,还是使用默认值。

讨论自建 String Pool

这一节我们来看下自己使用 HashMap 来实现 String Pool。

这里我们需要使用 WeakReference:

private static final WeakHashMap> pool

= new WeakHashMap>(1024);

private static String manualIntern(final String str) {

final WeakReference cached = pool.get(str);

if (cached != null) {

final String value = cached.get();

if (value != null) {

return value;

}

}

pool.put(str, new WeakReference(str));

return str;

}

我们使用 1000 * 1000 * 1000 作为入参 cnt 的值进行测试,分别测试 [1] 和 [2]:

private static void test(int cnt) {

final List lst = new ArrayList(1024);

long start = System.currentTimeMillis();

for (int i = 0; i < cnt; ++i) {

// [1]

lst.add(String.valueOf(i).intern());

// [2]

// lst.add(manualIntern(String.valueOf(i)));

if (i % 200000 == 0) {

System.out.println(i + 200000 + "; time = " + (System.currentTimeMillis() - start) / 1000.0 + " sec");

start = System.currentTimeMillis();

}

}

System.out.println("Total length = " + lst.size());

}

测试结果,2G 的堆大小,如果使用 String#intern(),大概在插入 3000万 数据的时候,开始进入大量的 FullGC。

而使用自己写的 manualIntern(),大概到 1400万 的时候,就已经不行了。

没什么结论,如果要说点什么的话,那就是不要自建 String Pool,没必要。

小结

记住有两个 JVM 参数可以设置:-XX:StringTableSize=N、-XX:+PrintStringTableStatistics

StringTableSize,在 Java 6 中,是 1009;在 Java 7 和 Java 8 中,默认都是 60013,如果有必要请自行扩大这个值。

参考资料

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java 获取常量池_Java 字符串常量池介绍 的相关文章

  • 2023电子信息工程毕设题目选题推荐

    文章目录 1前言 2 如何选题 3 选题方向 2 1 嵌入式开发方向 2 2 物联网方向 2 3 移动通信方向 2 4 人工智能方向 2 5 算法研究方向 2 6 移动应用开发方向 2 7 网络通信方向 3 4 学长作品展示 1前言 近期不
  • module ‘seaborn‘ has no attribute ‘histplot‘

    在jupyter notebook上 用seaborn画直方图的时候 遇到以下问题 解决方法 1 打开Anaconda Prompt 2 更新seaborn pip install U seaborn 3 关掉jupyter noteboo
  • Java连接数据库(自学笔记)

    一 六步骤 第一步 注册驱动 主要告诉Java程序连接哪种数据库 如MySql Orcale等 我自己连接的是MySql数据库 Driver driver new com mysql jdbc Driver DriverManager re
  • php7opcache使用,PHP7开启OPcache加速代码执行效率提升网站访问速度

    我们的网站访问速度是用户体念最重要的指标之一 网站内容再好打开速度过慢估计也是没有人愿意访问的 用wordpress企业主题搭建的网站常常有访问慢的问题 除了优化wordpress站内问题 服务器优化也是非常重要的 下面分享一个优化网站访问
  • 小程序成長之路_引入小程序自带icon 和 引用阿里图标(四)

    上篇我们已经成功填加tabBar 那么我们这篇就讲解一下 引用图标icon 小程序里有自己的图标供大家使用 但是图标有限 有很多都满足不了我们的需求 这次呢 给大家介绍一下阿里图标 里面有大量的图标供你选择 点击http www iconf
  • matlab 等间距抽稀算法

    目录 一 算法原理 1 原理概述 2 参考文献 二 代码实现 三 结果展示 一 算法原理 1 原理概述 等间距抽稀算法 在原始点云数据中设置采样间距为 n n n 首先在数据开始的 n n
  • 【Kubernetes资源篇】ConfigMap配置管理中心详解

    文章目录 一 ConfigMap配置中心理论知识 1 ConfigMap配置中心简介 2 ConfigMap局限性 二 创建ConfigMap的四种方式 1 第一种 通过命令行创建ConfigMap 2 第二种 通过指定文件创建Config
  • 蓝桥杯题库 算法提高非vip部分(C++、Java)代码实现(281-300)

    ADV 282 Island Hopping cpp include
  • 2021最新 Minecraft我的世界云服务器搭建教程

    一 购买服务器 熟练的朋友萌请跳过这里 首先我们先去al云或者tx云买个服务器 有学生优惠的话一年才120 当然最低配的单核2G 实测纯净服同时在线10个人无压力 这里以al云为例演示 首先 在搜索栏搜索学生优惠 点进去会是这样 然后我买的
  • 词的分布表示

    词的表示 One hot Representation 独热表示 苹果 表示为 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 优点 简介 缺点 词之间是孤立的 维数灾难 Distributed Representation
  • Writing a Scientific Research Report (IMRaD)学习笔记

    参考资料 Writing a Scientific Research Report IMRaD IMRAD Introduction Methods Results and Discussion How to Organize a Pape
  • pytorch从python转 c++涉及到的数据保存加载问题;libtorch

    pytorch 从python 转 c 涉及到的数据保存加载问题 1 torch nn Module 保存 state dict 无法被 c 访问 只能转化为 python字典 python代码 model ThreeLayer FCNN
  • MongoDB基础篇-08-spring-boot-starter-data-mongodb玩法

    文章目录 0 简述 1 依赖 2 yaml 3 MongoTemplate的使用 3 1 添加文档 3 2 修改文档 3 2 1 使用save修改 3 2 2 使用特定运算符进行更新 3 3 删除文档 3 3 1 根据主键删除 3 3 2
  • 性能测试包含哪些内容?

    性能测试是对软件产品在特定条件下的性能进行测试和评估的过程 性能测试的内容可以包括以下几个方面 1 负载测试 负载测试是指在特定条件下 对软件产品的性能进行测试和评估 测试人员可以通过模拟不同的用户数量 并发请求 访问频率等条件 来评估软件
  • 竞赛 基于机器视觉的停车位识别检测

    简介 你是不是经常在停车场周围转来转去寻找停车位 如果你的车辆能准确地告诉你最近的停车位在哪里 那是不是很爽 事实证明 基于深度学习和OpenCV解决这个问题相对容易 只需获取停车场的实时视频即可 该项目较为新颖 适合作为竞赛课题方向 学长
  • 中国互联网上市科技公司市值蒸发了多少亿?

    科技公司的市值断崖路与程序员的应对救生服 跌 跌 跌 2018 抱团取暖的科技公司 2018 年 对于中国互联网上市科技公司来说 是跌宕起伏的一年 这一年的前半段 以腾讯 阿里为首的头部公司 风光无两 市值屡创新高 腾讯最高市值突破 500
  • JDK8主要新特性介绍(二)

    1 6 类库新特性 java util stream 类库包新特性 java util Optional 类新特性 java util Base64 类新特性 Java time 类新特性 其它常用类新增特性 HashMap类性能提升 1
  • 详解C++类&对象(上篇),带你走进C++

    目录 一 面向对象 面向过程的认识 简单了解即可 逐步认识 二 类 2 1 类的引入 2 2 类的定义 1 struct 2 class 类的两种定义方式 2 3 封装 类的访问限定符 1 封装概念 2 类的访问限定符 2 4 类的作用域
  • 三体攻击问题(三维数组的前缀和 与 差分)(上篇)

    三体攻击问题 文章目录 三体攻击问题 题目详情 前言 预备知识 二维差分与前缀和 三维前缀和 定义 官方解释 自定义 三维数组求前缀和 三维差分 官方解释 自定义 跟二维差分类比 三维查分的求解 总结 题目详情 三体人将对地球发起攻击 为了

随机推荐

  • MES系统总体介绍

    MES系统总体 系统目标 MES系统通过控制包括物料 设备 人员 流程指令和设施在内的所有工厂资源 优化从定单到产品完成的整个生产活动 以最少的投入生产出最优的产品 实现连续均衡生产 MES系统通过与ERP DCS系统的全面集成 为企业搭建
  • protobuf 使用

    protobuf 生成类 生成的类中包含各个字段的get和set接口 使用SerializeToString可将class实例转换为protobuf二进制数据 ParseFromArray函数可将protobuf二进制反序列化到类的实例中
  • 为之前的commit添加签名signoff

    Centos yum install gnupg Ubuntu apt install gpg windows 听我一句劝 乖乖去用wsl 生成gpg gpg gen key gpg K 列出私钥 gpg k 列出公钥 导出公钥 gpg a
  • 计算机毕设Python+Vue在线答题系统(程序+LW+部署)

    项目运行 环境配置 Jdk1 8 Tomcat7 0 Mysql HBuilderX Webstorm也行 Eclispe IntelliJ IDEA Eclispe MyEclispe Sts都支持 项目技术 SSM mybatis Ma
  • Java第一次做项目学到技巧和知识

    第一个 定义项目return 在定义项目返回值的时候 可以利用泛型 Data public class R
  • python 猜数字游戏

    随机生成1 100的一个数 给7次猜的机会 猜对了或者7次都猜错了游戏结束 并弹出是否再来一次 每猜一次 给出提示High或者Low 并给出下次再猜的数字范围 guess number game import random def gues
  • Java怎么连接数据库 (使用技术及步骤原理,入门即会)

    文章说明 我们在这里讨论怎么去使用 了解它基本内容 认真看完 你就会了 连接时需要在 eclipse idea 加入它所需要的 jar包 依赖 下面讲为什么 你可以从网盘下载 也可以去官网搜索 下载所需的版本 链接 https pan ba
  • springboot返回object自动json转换以及fastjson转json时大写key转换问题

    springboot返回object自动json转换以及fastjson转json时大写key转换问题 当然 我们为了让用户更加便捷 我们增加了图片拖拽功能 项目的实体类中有一个LAY CHECKED An highlighted bloc
  • 验证邮箱地址的有效性javascript

    function isEmail email if email alert 邮箱不能为空 else if email split length 1 email split length 1 alert 邮箱地址必须包含 和 else let
  • 从 Salesforce 与 Veeva 合作历程,浅谈垂直型SaaS的初创及发展思路

    近日 相关传言称 美国最大客户关系管理SaaS供应商Salesforce中国区宣布解散 瞬间成为业内探讨热点 有媒体报道 Salesforce此举并非 退出中国市场 而是将中国区业务交给阿里云作为总代理 其相关产品依旧可以正常运作 Sale
  • Access Token 访问令牌 的获取与使用

    一 引用 三方库导入时 通常需要输入账号和令牌进行鉴权 账号为指定平台的 HTTP 克隆账号 访问令牌即 Access Token 本文介绍如何获取常见三方代码平台的Access Token Access Token 通常在代码平台的个人账
  • Spring Data JPA简介

    Spring Data JPA介绍 可以理解为JPA规范的再次封装抽象 底层还是使用了Hibernate的JPA技术实现 引用JPQL Java Persistence Query Language 查询语言 属于Spring整个生态体系的
  • 程序的二进制布局

    真正的程序员对自己所写的程序的每个字节都有所把握 虽然用一些OOP语言进行开发时 我们几乎不可能做到这一点 但我们还是应该对我们程序的二进制布局有个整体把握 在Linux环境中 无论是可重定位文件 o 共享目标文件 so 还是可执行文件都属
  • 一些有的没得小知识

    1 空间函数 st distance sphere st distance 计算距离 2 Stream流将List转Map 3 批量连表更新 1 空间函数 st distance sphere st distance 计算距离 st dis
  • Vision Transformers for Dense Prediction--阅读阶段

    论文相关申明 论文地址 article Ranftl2021 author Ren e Ranftl and Alexey Bochkovskiy and Vladlen Koltun title Vision Transformers f
  • Spring依赖注入之构造方法注入、set方法注入以及基于字段的方式注入比较

    1 spring提供了三种依赖注入的方式 先来看一下各个注入方式的实现方法以及各自的优缺点 1 基于字段注入的方式 该方式使用 Autowired方法就可以实现类的注入 Component public class ClassB Autow
  • Windows 10 修改远程桌面默认端口,增加远程连接的安全性

    一 打开远程桌面服务 1 1 文章的测试环境为Windows 10 专业版22H2 其他操作系统修改RDP 远程桌面服务 大同小异 1 2 右键开始 设置 搜索远程桌面 打开远程桌面 1 3 点击图中的高级设置 有微软的教程教你如何修改远程
  • 【自学Docker】Docker stats命令

    Docker stats命令 大纲 docker stats命令教程 docker stats 命令可以用于动态显示 Docker容器 的资源消耗情况 包括 CPU 内存 网络I O docker stats命令也可以指定已停止的容器 但是
  • windows在anaconda环境中安装dlib并且使用gpu加速

    1 首先在本地安装好anaconda 2 若不适用gpu加速的dlib 直接在anaconda的虚拟环境中 使用pip install dlib即可 3 若想使用gpu加速的dlib 例如调用dlib中的神经网络模型 首先 检查本地是否安装
  • java 获取常量池_Java 字符串常量池介绍

    本文将介绍 HotSpot 中的 String Pool 字符串常量池 相对是一篇比较简单的文章 大家花几分钟就看完了 在 Java 世界中 构造一个 Java 对象是一个相对比较重的活 而且还需要垃圾回收 而缓存池就是为了缓解这个问题的