1、为什么要使用intern()方法

intern方法设计的初衷是为了重用string对象,节省内存

用代码实例验证下

public class StringInternTest {
static final int MAX = 100000;
static final String[] arr = new String[MAX]; public static void main(String[] args) throws Exception {
//为长度为10的Integer数组随机赋值
Integer[] sample = new Integer[10];
Random random = new Random(1000);
for (int i = 0; i < sample.length; i++) {
sample[i] = random.nextInt();
}
//记录程序开始时间
long t = System.currentTimeMillis();
//使用/不使用intern方法为10万个String赋值,值来自于Integer数组的10个数
for (int i = 0; i < MAX; i++) {
//arr[i] = new String(String.valueOf(sample[i % sample.length]));
arr[i] = new String(String.valueOf(sample[i % sample.length])).intern();
}
System.out.println((System.currentTimeMillis() - t) + "ms");
System.gc();
}
}

这个例子,为了证明使用intern比不使用消耗的内存更少。

先定一个长度为10的Integer数组,并随机为其赋值,在通过for循环为长度为10w的String对象依次赋值,这些值都来自于Integer数组,两种情况分别运行,可通过Window ---> Preferences --> Java --> Installed JREs设置JVM启动参数为-agentlib:hprof=heap=dump,format=b,将程序运行后的hprof置于工程目录下。再通过MAT插件查看该hprof文件。

两次实验结果:

从运行结果来看,不使用intern的情况下,程序生成了101943个String对象,而使用intern,仅仅生成了1953个String对象,自然证明了intern节省内存的结论。

细心的同学发现了使用intern时执行时间是增加了,这是因为每次都是用了 new String后又进行intern()操作的耗时时间,但是不使用intern占用内存导致gc的时间远远大于这点时间。

2、深入认识intern方法

jdk1.7之后,常量池被放入到堆空间中,这导致intern函数的功能不同,具体怎么个不同法,且看看下面的代码,这个例子是网上流传较广的一个例子,分析图也是直接黏贴过来的。

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);

代码做调整之后的执行结果:

2.1 具体分析

JDK1.6的intern底层实现

JDK1.6以及之后的版本,常量池是放在Perm区(方法区)的,熟悉JVM的话这个是和堆是完全分开的。

使用引号声明的字符串都是会直接在字符串常量池中生成的,而new出来的String对象是放在堆空间中的,所以两者的内存地址肯定是不相同的,即使调用了intern方法也是不影响的。

intern方法在jdk1.6中的作用是:比如String s = new String("aaa"),再调用s.intern,此时返回值还是字符串“aaa”,表面上看起来好像这个方法没什么用处。但实际上,在JDK1.6中它做了个小动作:检查常量池里是否存在“aaa”这么一个字符串,如果存在,就返回池里的字符串,如果不存在,该方法会把“aaa”添加到字符串池中,然后再返回它的引用。

JDK1.7

针对JDK1.7以及以上的版本,我们将上面两段代码分开讨论。

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);
}

String s = newString("1"),生成了常量池中的“1” 和堆空间中的字符串对象。

s.intern(),这一行的作用是s对象去常量池中寻找后发现"1"已经存在于常量池中了。

String s2 = "1",这行代码是生成一个s2的引用指向常量池中的“1”对象。

结果就是 s 和 s2 的引用地址明显不同。因此返回了false。

String s3 = new String("1") + new String("1"),这行代码在字符串常量池中生成“1”。并在堆空间中生成s3引用指向的对象(内容为“11”),注意此时常量池中是没有“11”对象的。

s3.intern(),这一行代码,是将s3中的“11”字符串放入String常量池中,此时常量池中不存在“11”字符串,JDK1.6的做法是直接在常量池中生成一个“11”的对象,并返回该对象的引用。

但是在JDK1.7中,常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用直接指向s3引用的对象,也就是说s3.intern()==s3会返回true。

String s4 = “11”,这一行代码会直接去常量池中创建,但是发现已经有这个对象了,此时也就是指向s3引用对象的一个引用。因此s3 == s4返回了true。

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);
}

String s = new String("1"),生成了常量池中的“1”和堆空间中的字符串对象。

String s2 = "1",这行代码是生成一个s2的引用指向常量池中的"1"对象,但是发现已经存在了,那么就直接指向了它。

s.intern(),这一行在这里就没什么实际作用了,因为"1"已经存在了。

结果就是s和s2的引用地址明显不同。因此返回了false。

String s3 = new String("1") + new String("1"),这行代码在字符串常量池中生成"1",并在堆空间中生成s3引用指向的对象(内容为"11")。注意此时常量池中没有"11"对象的。

String s4 = "11",这一行代码会直接去生成常量池中的"11"。

s3.intern(),这一行在这里就没什么实际作用了,因为"11"已经存在了。

结果就是s3和s4的引用地址明显不同。因此返回了false。

String.intern() 方法__jdk1.6与jdk1.7/jdk1.8的不同的更多相关文章

  1. 常量池之String.intern()方法

    JDK7将String常量池从Perm区移动到了Java Heap区.在JDK1.6中,intern方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中的实例.但是在JDK1.7以后,Str ...

  2. String放入运行时常量池的时机与String.intern()方法解惑

    运行时常量池概述 Java运行时常量池中主要存放两大类常量:字面量和符号引用.字面量比较接近于Java语言层面的常量概念,如文本字符串.声明为final的常量值等. 而符号引用则属于编译原理方面的概念 ...

  3. 浅析String.intern()方法

    1.String类型“==”比较样例代码如下:package com.luna.test;public class StringTest { public static void main(Strin ...

  4. 字符串常量池和String.intern()方法在jdk1.6、1.7、1.8中的变化

    字符串常量池也是运行时常量池 jdk1.6中,它是在方法区中,属于“永久代” jdk1.7中,它被移除方法区,放在java堆中 jdk1.8中,取消了“永久代”,将常量池放在元空间,与堆独立了 pub ...

  5. String intern 方法 jdk中的描述

    一个初始为空的字符串池,它由类 String 私有地维护. 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中 ...

  6. 对于JVM中方法区,永久代,元空间以及字符串常量池的迁移和string.intern方法

    在Java虚拟机(以下简称JVM)中,类包含其对应的元数据,比如类的层级信息,方法数据和方法信息(如字节码,栈和变量大小),运行时常量池,已确定的符号引用和虚方法表. 在过去(当自定义类加载器使用不普 ...

  7. [String] intern()方法

    intern()方法设计的初衷,就是重用String对象,以节省内存消耗. JDK1.6以及以前版本中,常量池是放在 Perm 区(属于方法区)中的,熟悉JVM的话应该知道这是和堆区完全分开的. 使用 ...

  8. java 字符串String.intern()方法学习

    在jdk1.6与jdk1.7中,String类中的intern()方法实现的原理是有一些差异的.1.在jdk1.6中,intern()方法是先查找字符串常量池是否含有当前字符串,如果没有,那么就在字符 ...

  9. String intern()方法详解

    执行以下代码 String a1=new String("abc");       String a2=new String("abc");       Sys ...

随机推荐

  1. LInkedHashMap实现最近被使用(LRU)缓存

    在最近的面试中,我曾被多次问到,怎么实现一个最近最少使用(LRU)的缓存.缓存可以通过哈希表来实现,然而为这个缓存增加大小限制会变成另一个有意思的问题.现在我们看一下怎么实现. 最近最少使用缓存的回收 ...

  2. HTML5仿手机微信聊天界面

    HTML5仿手机微信聊天界面 这篇文章主要为大家详细介绍了HTML5仿手机微信聊天界面的关键代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下     给大家带来的是HTML5仿手机微信聊天界面, ...

  3. ubuntu 16.04 LTS - 谷歌拼音输入法

    https://blog.csdn.net/chengyq116/article/details/78638249 1. installation1.1 汉语语言包 sudo apt-get inst ...

  4. Mac系统下Mysql存储数据报错 ER_TRUNCATED_WRONG_VALUE_FOR_FIELD: Incorrect string value

    比如如下mysql操作插入数据: const mysql = require('mysql'); /* createConnection方法创建一个表示与Mysql数据库服务器之间连接的 Connec ...

  5. eclipse上配置svn

    eclipse里安装SVN插件,一般来说,有两种方式: 直接下载SVN插件,将其解压到eclipse的对应目录里 使用eclipse 里Help菜单的“Install New Software”,通过 ...

  6. openstack搭建之-glance配置(9)

    一. base节点配置 #设置数据库,创建glance数据库,并设置权限 mysql -u root -proot CREATE DATABASE glance; GRANT ALL PRIVILEG ...

  7. 第二部分之AOF持久化(第十一章)

    AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的.被写入AOF文件的所有命令都是以Redis的命令请求协议格式(纯文本)保存的. 一,AOF持久化的实现 1.命令追加 当AOF持 ...

  8. idea下创建maven聚合(子父级)项目,多模块项目

    IDEA下Maven多模块项目介绍和搭建 idea 创建maven聚合项目简洁教程(手把手入门,通俗易懂) 本人使用的是: intelj idea 创建聚合项目(典型web项目,包括子项目util.d ...

  9. Linux 学习 (十) 网络配置

    Linux网络管理 学习笔记 配置 IP 地址 ifconfig 命令临时配置 IP 地址 ifconfig eth0 192.168.0.200 netmask 255.255.255.0 #临时设 ...

  10. 前置通知也能对参数进行加工 通过joiPoint这个方法