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. Linux systemtap定位系统IO资源使用情况(ok)

    一.systemtap介绍 SystemTap是一个强大的调试工具,是监控和跟踪运行中的Linux 内核的操作的动态方法,确切的说应该是一门调试语言,因为它有自己的语法,也有解析.编译.运行等过程(准 ...

  2. 如何给python程序加密

    在实际的工作中,有时候我们需要部署自己的Python应用,但这时候我们并不希望别人能够看到自己的Python源程序.因此,我们需要为自己的源代码进行加密,Python已经为我们提供了这样一套工作机制. ...

  3. 利用BeEF REST API自动化控制僵尸主机

    本文首发Freebuf,属于原创奖励计划,未经许可禁止转载. http://www.freebuf.com/articles/network/137662.html 一. 前言 关于BeEF,不再多介 ...

  4. np.mgrid的用法

    功能:返回多维结构,常见的如2D图形,3D图形 np.mgrid[ 第1维,第2维 ,第3维 , …] 第n维的书写形式为: a:b:c c表示步长,为实数表示间隔:该为长度为[a,b),左开右闭 或 ...

  5. Linux内核入门到放弃-时间管理-《深入Linux内核架构》笔记

    低分辨率定时器的实现 定时器激活与进程统计 IA-32将timer_interrupt注册为中断处理程序,而AMD64使用的是timer_event_interrupt.这两个函数都通过调用所谓的全局 ...

  6. .Net Core HttpClient 忽略https证书提醒

    在测试中经常会遇到请求一些https的url,但又没有本地证书,这时候可以用下面的方法忽略警告 var httpclientHandler = new HttpClientHandler(); htt ...

  7. 通过secureCRT连接虚拟机VMware workstation问题记录

    很急没有使用虚拟机了,今天再登录的时候,发现用secureCRT连接不上VMware workstation 1.连接步骤: 1)打开secureCRT,点击+ 新建一个连接 2)按照流程一步一步配置 ...

  8. AWT的应用

    AWT(Abstract Window TookIT)是java用于创建用户界面和绘制图形的所有类 AWT组件和容器 AWT包含了很多类和接口,用于java Application的GUI(图形用户界 ...

  9. 小小知识点(五)——MATLAB对复数的操作

    MATLAB程序 a=3+4*i %复数 real(a) %求复数的实部 imag(a) %求复数的虚部 abs(a) %求复数的模 angle(a) %求复数的相位 conj(a) %求复数的复共轭 ...

  10. wepy项目创建

    全局安装wepy npm install wepy-cli -g 创建项目 wepy init standard mywepy 安装依赖 npm install 实时编译 wepy build --w ...