String是最常用的类之一,也许就是太常见了,因此导致经常忽略它存在,即使在观察堆得时候发现String类以及char[]所占的内存比较多的时候,也潜意识的跳过了它们。其实,String类以及char[]也还是有优化的空间的。

String的源码提供了非常丰富的方法,最多的就是对char[]数组的处理。对数组的处理,几乎就是复制数组和转码的各种组合。本来觉得看下去以及没啥惊喜了,指到发现以下这个方法:

    /*** Returns a canonical representation for the string object.* <p>* A pool of strings, initially empty, is maintained privately by the* class {@code String}.* <p>* When the intern method is invoked, if the pool already contains a* string equal to this {@code String} object as determined by* the {@link #equals(Object)} method, then the string from the pool is* returned. Otherwise, this {@code String} object is added to the* pool and a reference to this {@code String} object is returned.* <p>* It follows that for any two strings {@code s} and {@code t},* {@code s.intern() == t.intern()} is {@code true}* if and only if {@code s.equals(t)} is {@code true}.* <p>* All literal strings and string-valued constant expressions are* interned. String literals are defined in section 3.10.5 of the* <cite>The Java&trade; Language Specification</cite>.** @return  a string that has the same contents as this string, but is*          guaranteed to be from a pool of unique strings.*/public native String intern();

这一个看似普通的本地方法, 返回的是该Sting对象所指向的常量在常量池的地址。这里就不得不分析以下复杂的String对象关系了。也是导致为啥我们经常约定判断String对象是否相等时,不是用的“==”而是用equals()方法呢?是不是所有的时候2个String对象一定是不相等的呢?要说明这个问题,理解intern()方法就非常重要了。

intern()被调用的时候,首先是查看常量池中是否存在等于该String对象value的字符串,如果存在,则返回该字符串的地址,如果不存在就将该String对象的作为新的常量,那么新常量的地址和String对象的地址就是同一个了。这里有个前提就是在JDK1.7之后,常量池并入Java堆中。因此,上述的场景是处于JDK1.7之后的环境,我的验证环境为JDK1.8。

public class App {public static void main(String[] args) {String s1 = new String("aaa");String s2 = s1.intern();System.out.println(s1 == s2);}
}

根据上面对于intern()描述,第一印象结果应该是true,但是实际结果却是false。这个就很奇怪了,此时参考美团技术团队的文章终于找到了原因(https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html)。其实,

String s1 = new String("aaa");

中引入"aaa"作为参数的时候,因此在后面创建s1对象时,在常量池中时有了"aaa"常量,此时调用intern()返回的地址和s1不一致的。为了验证这个观点,写了以下代码:

public class App {public static void main(String[] args) {char[] chars = new char[]{'a', 'a', 'a'};String s1 = new String(chars);String s2 = s1.intern();System.out.println(s1 == s2);}
}

此时返回的结果就是true。

那么这个函数有什么用呢?其实就可以考虑用来优化Java堆。创建一个100万大小的数组,随机插入数字String,观察堆的情况和运行时间,记录3次运行数据。

public class App {private static long MAX = 100 * 10000;public static void main(String[] args) {long startTime = System.currentTimeMillis();List<String> strArr = new ArrayList<>();Random random = new Random();for (long i = 0; i < MAX; i++) {strArr.add(String.valueOf(random.nextInt() % (100 * 10)));
//            strArr.add(String.valueOf(random.nextInt() % (100 * 10)).intern());}long endTime = System.currentTimeMillis();System.out.println("Loop is end, Spending time is " + (endTime - startTime) / 1000.0 + "s");}
}
Java堆中String对象数量和大小以及运行时间【未使用intern()】
序号 数量 大小 运行时间
1 1,005,258 24,126kb 0.151s
2 1,005,246 24,126kb 0.164s
3 1,005,245 24,125kb 0.197s
平均值 1,005,250 24,126kb 0.171s
Java堆中String对象数量和大小以及运行时间【使用intern()】
序号 数量 大小 运行时间
1 45,077 1,081kb 0.215s
2 45,195 1,084kb 0.209s
3 45,077 1,081kb 0.204s
平均值 45,116 1082kb 0.209s

从上面的数据对比可以得知,合理使用intern()确实有降低堆大小的作用,但是也会带来额外的系统开销。因此intern()需要根据场景进行使用,不可滥用。

JDK 8源码解析——String中的intern()方法相关推荐

  1. JDK源码解析 Comparator 中的策略模式

    JDK源码解析 Comparator 中的策略模式.在Arrays类中有一个 sort() 方法,如下: public class Arrays{public static <T> voi ...

  2. 深入解析String中的intern

    转载自 深入解析String中的intern 引言 在 JAVA 语言中有8中基本类型和一种比较特殊的类型String.这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念.常 ...

  3. unity urp内置lit材质源码解析(中)

    上一篇(https://blog.csdn.net/qq_30100043/article/details/125725934)解析了内置shader lit的主文件和input文件,接下来,我们将视 ...

  4. 从源码解析-Android中Zygote进程是如何fork一个APP进程的

    zygote进程fork子进程 前言 强烈推荐 进程创建流程 APP启动第三方应用 startActivity startService sendBroadcast ContentResolver.q ...

  5. Spring5源码解析-Spring中的异步事件

    上一篇 Spring框架中的事件和监听器并未对Spring框架中的异步事件涉及太多,所以本篇是对其一个补充. 同步事件有一个主要缺点:它们在所调用线程的本地执行(也就是将所调用线程看成主线程的话,就是 ...

  6. 从源码解析-Android中View的绘制流程及performTraversals方法

    谈谈Activity的setContentView是怎么加载XML视图的 谈谈Activity的View怎么与View绘制工具ViewRootImpl关联的 在前面两篇文章中分析了View是如何跟绘制 ...

  7. 源码解析glibc中的pclose与fclose函数

    文章目录 pclose 和 fclose 的阻塞问题 测试代码 问题 pclose与fclose的关系 fclose函数的定义 pclose函数的定义 总结 fclose中的block 有嫌疑的地方 ...

  8. 【EventBus】EventBus 源码解析 ( 注册订阅者 | 订阅方法 | 查找订阅方法 )

    文章目录 一.EventBus 注册订阅者 二.订阅方法 三.查找订阅方法 findSubscriberMethods 方法 四.查找订阅方法 findUsingInfo 方法 五.查找订阅方法 fi ...

  9. vue源码解析:vue生命周期方法$destory方法的实现原理

    我们知道vue生命周期的最后一个阶段是销毁阶段,那么vue会调用自己的destory函数,那么$destory函数的实现原理是什么?且往下看. 用法: vm.$destroy() 作用: 完全销毁一个 ...

最新文章

  1. 【数据库】 兴唐第二十七节课只sql注入
  2. Racktables(一)的资产管理软件安装配置
  3. 王卡为何解封40g显示服务器开拆,腾讯大王卡40G流量用完了怎么办 腾讯大王卡解封以及解封后流量收费介绍...
  4. 支持向量机ModuleNotFoundError: No module named ‘sklearn.datasets.samples_generator‘
  5. 装配bean的三种方式
  6. mysql5.7.14安装版教程_MySQL5.7.14下载安装图文教程及MySQL数据库语句入门大全
  7. Pandas数据结构简介
  8. python课程_python课程大放送
  9. GUI中axes使用subplot时的清空及axes大小的固定的解决方法
  10. spring4+hibernate4配置sessionFactory错误
  11. 百度网盘kbengine - warring项目下载地址
  12. 谷歌何时停止Android更新,谷歌宣布Android Studio将停止为32位系统提供更新
  13. 服务器运算性能,服务器性能计算公式(20191116215459).pdf
  14. 记录一次归档日志爆满事件
  15. Python全栈开发——线程与进程的概念
  16. 基于VM10+Win7安装Mac OSX10.11 El Capitan
  17. 17-什么是资源服务器
  18. java 中的惰性初始化_java惰性初始化
  19. USB PD 等多协议快充方案
  20. 一种高性能计算机网络控制技术,一种高性能流量计算机的研制

热门文章

  1. 更改无线网卡mac地址后,无法连接到此无线网卡分享的热点
  2. 您的计算机上有多个活动的网络连接未修复,修复win10“我们无法设置移动热点因为你的电脑未建立以太网”的方法...
  3. pert计算公式期望值_PERT的应用
  4. Spring5——(一)spingIOC(入门介绍,spring创建bean,依赖,注入,注解方式)
  5. 万向节死锁(Gimbal Lock)
  6. 解决duilib使用zip换肤卡顿的问题:修改duilib并使用资源文件换肤
  7. messages报logger: Waiting minutes for filesystem containing crsctl
  8. c# 读取excels
  9. 2021年NOC复赛真题解析
  10. Spring beanid 同名覆盖问题解决