深入解析String intern()

2023-11-20


       在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。

8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种:

  • 直接使用双引号声明出来的String对象会直接存储在常量池中。
  • 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中

String intern的大体实现结构是:
JAVA 使用 jni 调用c++实现的StringTableintern方法, StringTableintern方法跟Java中的HashMap的实现是差不多的, 只是不能自动扩容。默认大小是1009。

要注意的是,String的String Pool是一个固定大小的Hashtable,默认值大小长度是1009,如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降(因为要一个一个找)。

在 jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。在jdk7中,StringTable的长度可以通过一个参数指定:

  • -XX:StringTableSize=99991

jdk6 和 jdk7 下 intern 的区别

很多 JAVA 程序员都做做类似 String s = new String("abc")这个语句创建了几个对象的题目。 这种题目主要就是为了考察程序员对字符串对象的常量池掌握与否。上述的语句中是创建了2个对象,第一个对象是"abc"字符串存储在常量池中,第二个对象在JAVA Heap中的 String 对象。

<span style="font-size:14px;">public static void main(String[] args) {
    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1");
    s3.intern();
    String s4 = "11";
    System.out.println(s3 == s4);
}</span>

打印结果是

  • jdk6 下false false
  • jdk7 下false true

然后将s3.intern();语句下调一行,放到String s4 = "11";后面。将s.intern(); 放到String s2 = "1";后面。是什么结果呢

<span style="font-size:14px;">public static void main(String[] args) {
    String s = new String("1");
    String s2 = "1";
    s.intern();
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1");
    String s4 = "11";
    s3.intern();
    System.out.println(s3 == s4);
}</span>

打印结果为:

  • jdk6 下false false
  • jdk7 下false false

####1,jdk6中的解释


注:图中绿色线条代表 string 对象的内容指向。 黑色线条代表地址指向。

如上图所示。首先说一下 jdk6中的情况,在 jdk6中上述的所有打印都是 false 的,因为 jdk6中的常量池是放在 Perm 区中的,Perm 区和正常的 JAVA Heap 区域是完全分开的。上面说过如果是使用引号声明的字符串都是会直接在字符串常量池中生成,而 new 出来的 String 对象是放在 JAVA Heap 区域。所以拿一个 JAVA Heap 区域的对象地址和字符串常量池的对象地址进行比较肯定是不相同的,即使调用String.intern方法也是没有任何关系的。

####2,jdk7中的解释

再说说 jdk7 中的情况。这里要明确一点的是,在 Jdk6 以及以前的版本中,字符串的常量池是放在堆的 Perm 区的,Perm 区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4m,一旦常量池中大量使用 intern 是会直接产生java.lang.OutOfMemoryError: PermGen space错误的。 所以在 jdk7 的版本中,字符串常量池已经从 Perm 区移到正常的 Java Heap 区域了。为什么要移动,Perm 区域太小是一个主要原因,当然据消息称 jdk8 已经直接取消了 Perm 区域,而新建立了一个元区域。应该是 jdk 开发者认为 Perm 区域已经不适合现在 JAVA 的发展了。

正式因为字符串常量池移动到 JAVA Heap 区域后,再来解释为什么会有上述的打印结果。


  • 在第一段代码中,先看 s3和s4字符串。String s3 = new String("1") + new String("1");,这句代码中现在生成了2最终个对象,是字符串常量池中的“1” 和 JAVA Heap 中的 s3引用指向的对象。中间还有2个匿名的new String("1")我们不去讨论它们。此时s3引用对象内容是"11",但此时常量池中是没有 “11”对象的。
  • 接下来s3.intern();这一句代码,是将 s3中的“11”字符串放入 String 常量池中,因为此时常量池中不存在“11”字符串,因此常规做法是跟 jdk6 图中表示的那样,在常量池中生成一个 "11" 的对象,关键点是 jdk7 中常量池不在 Perm 区域了,这块做了调整。常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向 s3 引用的对象。 也就是说引用地址是相同的。
  • 最后String s4 = "11"; 这句代码中"11"是显示声明的,因此会直接去常量池中创建,创建的时候发现已经有这个对象了,此时也就是指向 s3 引用对象的一个引用。所以 s4 引用就指向和 s3 一样了。因此最后的比较 s3 == s4 是 true。

  • 再看 s 和 s2 对象。 String s = new String("1"); 第一句代码,生成了2个对象。常量池中的“1” 和 JAVA Heap 中的字符串对象。s.intern(); 这一句是 s 对象去常量池中寻找后发现 “1” 已经在常量池里了。

  • 接下来String s2 = "1"; 这句代码是生成一个 s2的引用指向常量池中的“1”对象。 结果就是 s 和 s2 的引用地址明显不同。图中画的很清晰。

  • 来看第二段代码,从上边第二幅图中观察。第一段代码和第二段代码的改变就是 s3.intern(); 的顺序是放在String s4 = "11";后了。这样,首先执行String s4 = "11";声明 s4 的时候常量池中是不存在“11”对象的,执行完毕后,“11“对象是 s4 声明产生的新对象。然后再执行s3.intern();时,常量池中“11”对象已经存在了,因此 s3 和 s4 的引用是不同的。
  • 第二段代码中的 s 和 s2 代码中,s.intern();,这一句往后放也不会有什么影响了,因为对象池中在执行第一句代码String s = new String("1");的时候已经生成“1”对象了。下边的s2声明都是直接从常量池中取地址引用的。 s 和 s2 的引用地址是不会相等的。

小结
从上述的例子代码可以看出 jdk7 版本对 intern 操作和常量池都做了一定的修改。主要包括2点:

  • 将String常量池 从 Perm 区移动到了 Java Heap区
  • String#intern 方法时,如果存在堆中的对象,会直接保存对象的引用,而不会重新创建对象。

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

深入解析String intern() 的相关文章

  • “java.net.MalformedURLException:未找到协议”读取到 html 文件

    我收到一个错误 java net MalformedURLException Protocol not found 我想读取网络上的 HTML 文件 mainfest uses permission android name android
  • 是否有任何简单(且最新)的 Java 框架可用于在 Swing 应用程序中嵌入电影?

    我正在构建一个小型 Swing 应用程序 我想在其中嵌入一部电影 重要的是 这个应用程序是一个 WebStart 应用程序 并且该库应该能够打包在我启动的 jnlp 中 即 不依赖于本机库 我知道并尝试过 JMF 但我认为与其他框架相比 其
  • 打印星号的 ASCII 菱形

    我的程序打印出这样的钻石 但只有当参数或菱形的每一面为4 例如如果我输入6 底部三角形的间距是错误的 我一直在试图找出答案 当参数改变时 底部的三角形不会改变 只有顶部的三角形会改变 它只适用于输入4 public static void
  • Spring Boot自动装配存储库始终为空[重复]

    这个问题在这里已经有答案了 每次我进入我的服务类时 存储库似乎都没有自动连接 因为它不断抛出 NullPointerException 谁能帮我检查一下我缺少什么吗 这是我的代码 演示应用程序 java package com exampl
  • 通往楼梯顶部的可能路径

    这是一个非常经典的问题 我听说谷歌在他们的面试中使用过这个问题 问题 制定一个递归方法 打印从楼梯底部到楼梯顶部的所有可能的独特路径 有 n 个楼梯 您一次只能走 1 步或 2 步 示例输出 如果它是一个有 3 级楼梯的楼梯 1 1 1 2
  • 是否可以使用 Flying Saucer (XHTML-Renderer) 将 css 解析为类路径资源?

    我正在尝试将资源打包到 jar 中 但我无法让 Flying Saucer 在类路径上找到 css 我无法轻松构建 URL 来无缝解决此问题 https stackoverflow com questions 861500 url to l
  • 如何在代理后面安装 Eclipse Neon

    对于 Neon Eclipse 附带了一个安装程序 我在安装程序中找不到任何配置菜单 我的java版本是 java version java version 1 8 0 72 Java TM SE Runtime Environment b
  • Kotlin 未解决的参考:CLI 上 gradle 的 println

    放一个printlnkotlin 函数返回之前的语句会崩溃 堆栈跟踪 thufir dur NetBeansProjects kotlin thufir dur NetBeansProjects kotlin gradle clean bu
  • Spring Security SAML2 使用 G Suite 作为 Idp

    我正在尝试使用 Spring Security 5 3 3 RELEASE 来处理 Spring Boot 应用程序中的 SAML2 身份验证 Spring Boot 应用程序将成为 SP G Suite 将成为 IDP 在我的 Maven
  • 删除Android所有语言中的字符串

    我有一个包含多个翻译的应用程序 我想删除一些字符串 我怎样才能重构并删除它们一次 例如在默认情况下strings xml文件并自动将删除传播到其他翻译的其他 strings xml 文件 您可以通过 Android Studio 中的 翻译
  • Java Swing For mac 中的 DJ Native Swing 浏览器

    我有一个用 Swing 制作的 Java 应用程序 并且使用了一个 DJ Native Swing 浏览器 当我尝试在 OS X 上使用它时 它抛出了一个NoClassDefFoundError尽管我添加了 swt jar 但始终如此 有人
  • 在另一个模块中使用自定义 gradle 插件模块

    我正在开发一个自定义插件 我希望能够在稍后阶段将其部署到存储库 因此我为其创建了一个独立的模块 在对其进行任何正式的 TDD 之前 我想手动进行某些探索性测试 因此 我创建了一个使用给定插件的演示模块 到目前为止 我发现执行此操作的唯一方法
  • GWT 2.3 开发模式 - 托管模式 JSP 编译似乎不使用 java 1.5 兼容性

    无法编译 JSP 类 生成的 servlet 错误 DefaultMessage 上次更新 0 日期 中 0 时间 HH mm ss z 语法 错误 注释仅在源级别为 1 5 时可用 在尝试以开发模式在 Web 浏览器中打开我的 gwt 模
  • Java实现累加器类,提供Collector

    A Collector具有三种通用类型 public interface Collector
  • Freemarker 和 Struts 2,有时它计算为序列+扩展哈希

    首先我要说的是 使用 Struts2 Freemarker 真是太棒了 然而有些事情让我发疯 因为我不明白为什么会发生这种情况 我在这里问是因为也许其他人有一个想法可以分享 我有一个动作 有一个属性 说 private String myT
  • 在 SWT/JFace RCP 应用程序中填充巨大的表

    您将如何在 SWT 表中显示大量行 巨大是指超过 20K 行 20 列的东西 不要问我为什么需要展示那么多数据 这不是重点 关键是如何让它尽可能快地工作 这样最终用户就不会厌倦等待 每行显示某个对象的实例 列是其属性 一些 我想使用 JFa
  • Docker 和 Eureka 与 Spring Boot 无法注册客户端

    我有一个使用 Spring Boot Docker Compose Eureka 的非常简单的演示 我的服务器在端口 8671 上运行 具有以下应用程序属性 server port 8761 eureka instance prefer i
  • Java中HashMap和ArrayList的区别?

    在爪哇 ArrayList and HashMap被用作集合 但我不明白我们应该在哪些情况下使用ArrayList以及使用时间HashMap 他们两者之间的主要区别是什么 您具体询问的是 ArrayList 和 HashMap 但我认为要完
  • 泛型、数组和 ClassCastException

    我想这里一定发生了一些我不知道的微妙事情 考虑以下 public class Foo
  • 在java中使用多个bufferedImage

    我正在 java 小程序中制作游戏 并且正在尝试优化我的代码以减少闪烁 我已经实现了双缓冲 因此我尝试使用另一个 BufferedImage 来存储不改变的游戏背景元素的图片 这是我的代码的相关部分 public class QuizApp

随机推荐

  • 0基础,自学3个月转行软件测试,从月薪3k到12k我整理的超全学习指南

    大家好我是小娜 现在是一名软件测试测试工程师 工资在1w2阶段还需努力 我大学期间学的是电气工程及其自动化专业 可以说是一事无成哈哈 大学三天打鱼 两天筛网导致专业水平学的不怎么样 毕业后从事了一份销售 买房子 的工作 但是 销售工作压力大
  • GIT代码仓库迁移

    GIT代码仓库迁移 保留原有的提交记录 查看原有提交路径 git remote v 变更为新的提交路径 git remote set url origin 新路径 拉取新路径下的内容 允许不相关历史提交 并强制合并 git pull ori
  • 亲密关系沟通-【正向情绪】创造一场愉快的沟通体验

    为什么有的时候聊天越聊越开心 有时候却让你意兴阑珊 正向情绪体验 积极议题 充分发挥 谁都喜欢被看见 让他不断得到关注 得到你持续的关注 你知道吗 我这个月业绩第一 哇 你超棒的 哪里哪里 话题聊不下去 你给他夸奖 他只能谦虚 天哪你怎么做
  • [转]使用Midjourney命令输出稳定的漫画角色

    转载自 用对Midjourney命令你也可以输出稳定的漫画角色 qq com 昨天的文章已经教会了大家如何成为15分钟快速制作一幅小漫画 有兴趣的可以翻阅上一篇文章 但是不少同学会发现 Midjourney的随机性比较强 即使输入相同的命令
  • 转载:《七周成为数据分析师》

    百日计划第一周总结 1 计划 1 彻底结束之前预定暑假完成的天善学院课程 七周数据分析师 2 总结 七周数据分析师 2 完成情况 1 完成 七周成为数据分析师 任务 2 周总结与 七周数据分析师 一起完成 七周数据分析师 总结 第一周 数据
  • 【Python零基础入门篇 · 17】:模块、模块的使用、过滤执行代码写法、包的使用

    文章目录 模块 内置模块 第三方模块 自定义模块 模块的使用 import 模块名 导入自定义模块 from import 模块起别名 as 导入多模块 模块 模块 就是 py文件 里面定义了一些函数和变量 需要的时候就可以导入这些模块 执
  • 零基础Java详细的jdk的下载以及安装配置

    文章目录 一 下载jdk 二 配置jdk 三 查看配置是否成功 一 下载jdk 1 登录oracle官网 2 3 4 点进去往下拉 会有jdk新版本 接受许可协议 直接下载 zip压缩文档就行 也可以直接下载 exe安装版本 5 找到压缩包
  • FPGA实现VGA显示图片

    利用FPGA在带有VGA接口的液晶显示器上显示图片 电路原理图 端口说明 VGA R2 VGAB0的8个端口位VGA的RGB数据位 VGA HS为行同步信号 VGA VS为场同步信号 以分辨率为640x480为例 刷新速率为60Hz 每幅图
  • [Python学习] 专题六.局部变量、全局变量global、导入模块变量

    定义在函数内的变量有局部作用域 在一个模块中最高级别的变量有全局作用域 本文主要讲述全局变量 局部变量和导入模块变量的方法 参考 Python核心编程 第二版 一 局部变量 声明适用的程序的范围被称为了声明的作用域 在一个过程中 如果名字在
  • 基于芯科Ember SDK开发的ZigBee ota client验证固件时崩溃(Reset info: 0x0A (FLT)), SDK Bug导致!

    现象 ZigBee子设备新固件下载完成后 准备校验其合法性 代码运行至检验部分 MCU崩溃重启 日志如下 RECV ASCII gt Processing message len 33 profile 0104 cluster 0019 T
  • IOS presentViewController没办法全屏的问题

    需要弹出的视图控制器的属性设置为0即可 mImagePick modalPresentationStyle 0
  • sklearn包MLPClassifier的使用详解+例子

    MLPClassifier 参数说明 hidden layer sizes 元组形式 长度n layers 2 默认 100 第i元素表示第i个神经元的个数 activation identity logistic tanh relu 默认
  • Vue3中的pinia使用(收藏版)

    1 pinia介绍 个人网站 紫陌 笔记分享网 想寻找共同学习交流 共同成长的伙伴 请点击 前端学习交流群 pinia 是 Vue 的存储库 它允许您跨组件 页面共享状态 就是和vuex一样的实现数据共享 依据Pinia官方文档 Pinia
  • 【Python】Python 模式匹配与正则表达式

    Python 模式匹配与正则表达式 1 模式匹配与正则表达式 你可能熟悉文本查找 即按下Ctrl F 输入你要查找的词 正则表达式 更进一步 它们让你指定要查找的 模式 你也许不知道一家公司的准确电话号码 但如果你住在美国或加拿大 你就知道
  • 21,verilog之宏define介绍

    注 学习 交流就在博主的个人weixin公众号 FPGA动力联盟 留言或直接 博主weixin fpga start 私信 宏define提供用一个相对简单的文字来表示一大段真正有意义的文字作用 换句话说 就是综合软件见到定义的宏 就用这个
  • uni-app页面点击tab左右滑动(swiper)

    uni app基于
  • 自动化测试工具——selenium 用前须知

    OK 经过上的过程 我相信你一定做出的相应的选择 如果你选择的是selenium 工具 那么接着往下阅读 首选你在开始selenium之前 需要花一到两个月时间去学一门语言 这里是根据没有语言基础的同学而定的 我推荐ruby python
  • swing重定向输出到jtextArea

    import java awt Font import java io IOException import java io OutputStream import java io PrintStream import javax swin
  • Android Studio 中模拟器无法打开,提示Error launching emulator

    一 遇到的问题 运行模拟器时 提示 Error launching emulator 二 解决方法 打开SDK Manager 点击取消Android Emulator 然后重新运行 会提示下载一些文件 按着操作即可 如果没问题就不需要取消
  • 深入解析String intern()

    在 JAVA 语言中有8中基本类型和一种比较特殊的类型String 这些类型为了使他们在运行过程中速度更快 更节省内存 都提供了一种常量池的概念 常量池就类似一个JAVA系统级别提供的缓存 8种基本类型的常量池都是系统协调的 String类