返回顶部
首页 > 资讯 > 后端开发 > Python >详解JavaString中intern方法的原理与使用
  • 756
分享到

详解JavaString中intern方法的原理与使用

2024-04-02 19:04:59 756人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录简介常量池简介intern方法简介(jdk7)原理(JDK6与JDK7)例程测试例程分析jdk1.6jdk1.7应用实例简介 本文介绍Java的String的intern方法的原

简介

本文介绍Java的String的intern方法的原理。

常量池简介

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

String的常量池的主要使用方法有两种:

直接使用双引号声明出来的String对象会直接存储在常量池中。

如果不是用双引号声明的String对象,可以使用String提供的intern方法将其放到常量池。

intern方法简介(JDK7)

原型:public native String intern();

说明:

字符串常量池中查询当前字符串是否存在(通过equals判断)。

  • 如果存在,返回常量池中的字符串引用。
  • 如果不存在,把这个String对象引用存到常量池,然后返回这个String对象的引用。

返回值:都是返回String变量对应的字符串常量池的引用。

示例

package com.example;
 
public class Demo {
    public static void main(String argv[]) {
        String s = "test";
        System.out.println(s == s.intern());
    }
}

JDK6及之前:输出false

JDK7及之后:输出true

原理(JDK6与JDK7)

常量池里的字符串的由来

JDK6及以前调用String.intern()

  • 若常量池中有,则返回常量池中这个字符串的引用
  • 若常量池中没有,则拷贝一份对象,放到常量池(永久代)中;返回值是常量池(永久代)中对应字符串实例的引用。

JDK7及以后调用String.intern()

  • 若常量池中有,则返回常量池中这个字符串的引用
  • 若常量池中没有,则拷贝一份引用,放到常量池(堆)中;(JDK1.7将String常量池从Perm区移动到了Java Heap区)

例程测试

例程1: 

package org.example.a;
 
public class Demo {
    public static void main(String argv[]) {
        String s1 = new String("1");
        s1.intern();
        String s2 = "1";
        System.out.println(s1 == s2);
 
        String s3 = new String("1") + new String("1");
        s3.intern();
        String s4 = "11";
        System.out.println(s3 == s4);
    }
}

结果

jdk6:false false
jdk7:false true
jdk8:false true

例程2: 

package org.example.a;
 
public class Demo {
    public static void main(String argv[]) {
        String s1 = new String("1");
        s1.intern();
        String s2 = "1";
        System.out.println(s1 == s2);
 
        String s3 = new String("1") + new String("1");
        String s4 = "11";
        s3.intern();
        System.out.println(s3 == s4);
    }
}

上述代码第二部分有个对调。

结果

jdk6:false false
jdk7:false false
jdk8:false false

例程分析

下边图中:绿色线条代表 String 对象的内容指向。 红色线条代表地址指向。

jdk1.6

例程1与例程2的分析

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

jdk1.7

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

例程1的分析

1.String s1 = new String("1"); 

分析:这行代码生成了2个对象(常量池中的“1” 和 JavaHeap 中的字符串对象)。s.intern(); 这一句是 s1 对象去常量池中寻找后发现 “1” 已经在常量池里了。

此时s1指向的是Java Heap中的字符串对象。

2.String s2 = "1"; 

分析:这行代码生成一个 s2的引用指向常量池中的“1”对象。 结果就是 s1 和 s2 的引用地址不同。

3.String s3 = new String("1") + new String("1");

分析:这行代码生成了2个对象(字符串常量池中的“1” 和 Java Heap中的 s3 引用指向的对象“11”(中间还有2个匿名的new String("1")我们不讨论它)。
此时s3 是Java Heap中的字符串对象的引用,对象内容是”11″,此时常量池中是没有 “11”对象的。

4.s3.intern();

分析:这行代码将 s3中的"11"字符串放入String 常量池中,因为此时常量池中不存在"11"字符串,因此常规做法是跟 jdk6 图中表示的那样,在常量池中生成一个"11"的对象,关键点是 jdk7 中常量池不在Perm区域,而是在堆中了。常量池中不需再存储一份对象了,可以直接存储堆中的引用。这份引用指向s3引用的对象。 也就是说引用地址是相同的。

此时,s3是Java Heap中的字符串对象的引用,对象内容是”11″,此时常量池中是有 “11”对象,它保存的就是s3引用地址。

5.String s4 = "11"; 

这行代码”11″是显式声明的,因此会直接去常量池中创建,创建时发现已经有这个对象了。

此时:s4 == 常量池的“11”对象引用 == s3引用对象的引用

例程2的分析

String s1 = new String("1");

s1.intern();

String s2 = "1";

分析:s1.intern();,这一句往后放也不会有什么影响了,因为对象池中在执行第一句代码String s = new String("1");的时候已经生成“1”对象了。下边的s2声明都是直接从常量池中取地址引用的。 s1 和 s2 的引用地址是不会相等的。

String s3 = new String("1") + new String("1");

分析:这行代码生成了2个对象(字符串常量池中的“1” 和 Java Heap中的 s3 引用指向的对象“11”(中间还有2个匿名的new String("1")我们不讨论它)。

此时s3 是Java Heap中的字符串对象的引用,对象内容是”11″,此时常量池中是没有 “11”对象的。

String s4 = "11";

分析:声明 s4 的时候常量池中是不存在“11”对象的,执行完后,s4是常量池里“11“对象的引用。

s3.intern(); 

分析:此时常量池中“11”对象已经存在了,不会有任何操作,s3仍然是堆中String对象的引用。因此 s3 != s4

应用实例

package org.example.a;
 
import java.util.Random;
 
public class Demo {
    static final  int MAX = 1000 * 10000;
    static final String[] arr = new String[MAX];
    public static void main(String argv[]) {
        Integer[] DB_DATA = new Integer[10];
        Random random = new Random(10 * 10000);
        for(int i = 0; i < DB_DATA.length; i++){
            DB_DATA[i] = random.nextInt();
        }
 
        long t = System.currentTimeMillis();
        for(int i = 0; i < MAX; i++){
            //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
            arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
        }
 
        System.out.println((System.currentTimeMillis() -t) + "ms");
        System.GC();
    }
}

上述代码是一个演示代码,其中有两条语句不一样,一条是使用 intern,一条是未使用 intern。

运行的参数是:-Xmx2g -Xms2g -Xmn1500M

不用intern

2160ms

使用intern

826ms

通过上述结果,我们发现不使用 intern 的代码生成了1000w 个字符串,占用了大约640m 空间。 使用了 intern 的代码生成了1345个字符串,占用总空间 133k 左右。其实通过观察程序中只是用到了10个字符串,所以准确计算后应该是正好相差100w 倍。虽然例子有些极端,但确实能准确反应出 intern 使用后产生的巨大空间节省。

细心的同学会发现使用了 intern 方法后时间上有了一些增长。这是因为程序中每次都是用了 new String 后, 然后又进行 intern 操作的耗时时间,这一点如果在内存空间充足的情况下确实是无法避免的,但我们平时使用时,内存空间肯定不是无限大的,不使用 intern占用空间导致 JVM 垃圾回收的时间是要远远大于这点时间的。 毕竟这里使用了1000w次intern 才多出来1秒钟多的时间。

到此这篇关于详解Java String中intern方法的原理与使用的文章就介绍到这了,更多相关Java String intern方法内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 详解JavaString中intern方法的原理与使用

本文链接: https://lsjlt.com/news/149189.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 详解JavaString中intern方法的原理与使用
    目录简介常量池简介intern方法简介(JDK7)原理(JDK6与JDK7)例程测试例程分析jdk1.6jdk1.7应用实例简介 本文介绍Java的String的intern方法的原...
    99+
    2024-04-02
  • JDK8中String的intern()方法实例详细解读
    目录一、前言二、图文理解String创建对象1.例子一2.例子二3.例子三4.例子四5.例子五6.例子六三、深入理解intern()方法1. 源码查看2. 例子一3. 例子二4. 例...
    99+
    2024-04-02
  • Java String中intern方法怎么使用
    今天小编给大家分享一下Java String中intern方法怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧...
    99+
    2023-06-30
  • React.JS中JSX的原理与使用方法
    这篇文章主要介绍“React.JS中JSX的原理与使用方法”,在日常操作中,相信很多人在React.JS中JSX的原理与使用方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”...
    99+
    2024-04-02
  • Exchanger的原理与使用方法
    本篇内容介绍了“Exchanger的原理与使用方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2024-04-02
  • JUC中的wait与notify方法实现原理详解
    目录1.Object中的wait()实现原理2.Object对象中的wait,notify,notifyAll的理解3.wait 实战1.Object中的wait()实现原理 在进行...
    99+
    2023-03-10
    JUC wait与notify JUC wait JUC notify
  • 详解C++中菱形继承的原理与解决方法
    目录菱形继承形成原因应对方案虚继承 vitrual解决二义性变量内存布局–虚基表感悟菱形继承形成原因 多继承,呈菱形状 菱形继承代码: class A { publi...
    99+
    2023-02-01
    C++菱形继承原理 C++菱形继承解决方法 C++菱形继承
  • Spring中拦截器的原理与使用方法
    这篇文章主要讲解了“Spring中拦截器的原理与使用方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring中拦截器的原理与使用方法”吧!1.Spring中的拦截器在web开发中,拦截...
    99+
    2023-07-02
  • 详解Python中递归函数的原理与使用
    目录什么是递归函数递归函数的条件定义一个简单的递归函数代码解析内存栈区堆区死递归尾递归实例什么是递归函数 如果一个函数,可以自己调用自己,那么这个函数就是一个递归函数。 递归,递就是...
    99+
    2024-04-02
  • Mybatis-Plus中and()和or()的使用与原理详解
    目录一. 简单无优先级连接(即无括号的sql语句)1. 与连接 and()2. 或连接 or()二. 复杂有优先级的的连接补充:MybatisPlus中and和or的组合使用总结一....
    99+
    2024-04-02
  • Python中弱引用的神奇用法与原理详解
    目录背景典型用法工作原理实现细节总结背景 开始讨论弱引用( weakref )之前,我们先来看看什么是弱引用?它到底有什么作用? 假设我们有一个多线程程序,并发处...
    99+
    2024-04-02
  • 一文详解Python中生成器的原理与使用
    目录什么是生成器迭代器和生成器的区别创建方式生成器表达式基本语法生成器函数yield关键字yield和returnyield的使用方法生成器函数的基本使用send的使用可迭代对象的优...
    99+
    2024-04-02
  • 详解C++11中绑定器bind的原理与使用
    目录bind1st和bind2nd什么时候会用到函数对象绑定器bind1st和bind2nd的底层实现原理bind1st和bind2nd什么时候会用到 bind用于绑定可调用 (Ca...
    99+
    2022-12-09
    C++11绑定器bind C++11绑定器 C++11 bind
  • Java中Prime算法的原理与实现详解
    目录Prim算法介绍1.点睛2.算法介绍3. 算法步骤4.图解Prime 算法实现1.构建后的图2.代码3.测试Prim算法介绍 1.点睛 在生成树的过程中,把已经在生成树中的节点看...
    99+
    2024-04-02
  • SpringMVC中常用注解与使用方法详解
    MVC简介 MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写, 是一种用于设计创建 Web 应...
    99+
    2024-04-02
  • Mysql账户管理原理与实现方法详解
    本文实例讲述了Mysql账户管理原理与实现方法。分享给大家供大家参考,具体如下: 账户管理 在生产环境下操作数据库时,绝对不可以使用root账户连接,而是创建特定的账户,授予这个账户特定的操作权限,然后连接...
    99+
    2024-04-02
  • 详解JWT与Token的应用与原理
    目录JWT的应用Token的组成原理JWT对称加密JWT非对称加密生成私钥和公钥前言:JWT全称“JSON Web Token”,是实现Token的机制。官网...
    99+
    2023-05-16
    JWT应用与原理 Token应用与原理
  • Vue中this.$nextTick()的理解与使用方法
    目录this.$nextTick()原理:1.Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。2.Vue官网3.等价转换,如果thi...
    99+
    2024-04-02
  • 详解Go语言中泛型的实现原理与使用
    目录前言问题解决方法类型约束重获类型安全泛型使用场景性能虚拟方法表单态化Go 的实现结论前言 原文:A gentle introduction to generics in G...
    99+
    2022-06-07
    详解go语言 GO 泛型 go语言
  • Java中ThreadLocal的用法和原理详解
    目录用法实现原理内存泄漏用法 隔离各个线程间的数据避免线程内每个方法都进行传参,线程内的所有方法都可以直接获取到ThreadLocal中管理的对象。 package com.exam...
    99+
    2023-05-15
    Java ThreadLocal用法 Java ThreadLocal原理 Java ThreadLocal
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作