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. 3.15 总结,初始java

  2. tmux编译安装

    依赖libevent,ncurses libevent 2.x 官网:http://libevent.org 下载:https://github.com/libevent/libevent/relea ...

  3. Linux内核入门到放弃-模块-《深入Linux内核架构》笔记

    使用模块 依赖关系 modutils标准工具集中的depmod工具可用于计算系统的各个模块之间的依赖关系.每次系统启动时或新模块安装后,通常都会运行该程序.找到的依赖关系保存在一个列表中.默认情况下, ...

  4. DisplayAttribute没作用,why?

    namespace WebBulletinBoard.DataAccess { using System; using System.ComponentModel.DataAnnotations; u ...

  5. [Alpha阶段]第二次Scrum Meeting

    Scrum Meeting博客目录 [Alpha阶段]第二次Scrum Meeting 基本信息 名称 时间 地点 时长 第二次Scrum Meeting 19/04/04 大运村寝室6楼 90min ...

  6. 搭建Android手机服务器(一)

    自从上学期的软件工程课之后,我们团队设计的抢答器一直想把服务端移到移动端.所以,我一直在考虑如何把手机作为一台服务器.今天我主要讲解一下,对于没有android真机只有模拟器的,如何设置模拟器,使得在 ...

  7. json文件格式详解

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式. 易于人阅读和编写.同时也易于机器解析和生成. 它基于JavaScript Programming Lan ...

  8. EntityFramework优化:第一次启动优化

    1. 预先生成视图 通过代码的方式来预先生成视图,要求EntityFramework是6.0及以上版本. 控制台程序: using System.Data.Entity.Infrastructure; ...

  9. git仓库构建小记

    1.新建 .git 文件夹 约定的文件目录下,新建 .git 文件夹 mkdir test.git 2.初始化服务端仓库 git init --bare test.git 此时进入 test.git ...

  10. mpvue——引入echarts打包vendor过大

    前言 有一个项目需要引入图表,当时有两种选择一种是mpvue-echarts,一种是F2,而我经过踩坑之后依然决然的选择了mpvue-echarts,简单快捷容易上手,主要之前用过比较熟悉. 问题 | ...