在jdk7下慎用String.intern()作为synchronized的对象锁
有一段这样的代码:
for (int i = 0; i < 10000000; i++) {
("bluedavy" + i).intern();
if(i % 100 == 0)
Thread.sleep(1);
}
大家可以分别用这段代码在JDK 6里和JDK 7里跑跑看看,会有什么不同。
上面的代码在JDK 7里执行时比JDK 6将会更多的触发Young GC和Full GC,原因请见这段描述:
In JDK 7, interned strings are no longer allocated in the permanent generation of the Java heap, but are instead allocated in the main part of the Java heap (known as the young and old generations), along with the other objects created by the application. This change will result in more data residing in the main Java heap, and less data in the permanent generation, and thus may require heap sizes to be adjusted. Most applications will see only relatively small differences in heap usage due to this change, but larger applications that load many classes or make heavy use of the String.intern() method will see more significant differences.
简单来说就是在JDK 7里String.intern生成的String不再是在perm gen分配,而是在Java Heap中分配,因此自然上面的这段代码在JDK 7里会产生更为严重的Young GC和Full GC,就像上面这段描述里说的一样,这个变化对于装载了很多类的应用估计还是会有些明显的影响,对反射使用多的其实也会有些影响。
关于这个变化,在Stack Overflow上还有个有趣的case:
class Test
{
public static void main(String... args)
{
String s1="Good";
s1=s1+"morning";
System.out.println(s1.intern()); String s2="Goodmorning";
System.out.println(s1==s2);
}
}
上面这段代码在目前的JDK 6里和JDK 7里竟然会不同,JDK6里会输出false,而JDK 7会输出true,原因是JDK 6中执行String.intern时需要将此字符串的实例cp到perm并生成一个新的String对象,因此上面的s1和s2的对象地址是不同的,而在JDK 7中,执行String.intern时,则只是在String Pool中记录此字符内容对应的字符串实例。
尽管在比较字符串时,一般都不会用 == 去比较,但还是要知道String.intern的这个变化。
String.intern放进的String Pool是一个固定大小的Hashtable,默认值是1009,如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降(因为要一个一个找)。
现在仔细想想,看来当时这个case并不是因为频繁抛异常造成的,而是因为这个case中抛的是NoSuchMethodException,而抛这个异常的原因是因为调用了Class.getMethod找方法没找到,在class.getMethod这方法的实现里会调用name.intern,而很不幸的是这个case里传入的name会根据请求而变,因此导致了String Pool中放入了很多的String,hash冲突严重,链表变长,从而才导致了造成了String.intern过程变得比较耗CPU。
JDK为了解决这个问题,在6u32以及JDK 7的版本里支持了StringTable大小的配置功能,可在启动参数上增加-XX:StringTableSize来设置,具体的信息见:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962930
不过目前JDK未提供方法来查看StringTable中各桶的链表长度,如果提供这个的话就更好了
以上内容转载自:http://www.chepoo.com/jdk7-string-intern-change.html
了解String.intern()在jdk7的变化后,我们为了在单例类里并发时对同一个用户保证操作原子性,会加同步块,例如:
synchronized (("" + userId).intern()) {
// TODO:something
}
这个在jdk6里问题不算大,因为String.intern()会在perm里产生空间,如果perm空间够用的话,这个不会导致频繁Full GC,
但是在jdk7里问题就大了,String.intern()会在heap里产生空间,而且还是老年代,如果对象一多就会导致Full GC时间超长!!!
慎用啊!解决办法?终于找到了。
这里要引用强大的google-guava包,这个包不是一般的强大,是完全要把apache-commons*取缔掉的节奏啊!!!
Interner<String> pool = Interners.newWeakInterner();
synchronized ( pool.intern("BizCode"+userId)){
//TODO:something
}
API文档:http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Interners.html
代码参考TEST类:https://chromium.googlesource.com/external/guava-libraries/+/release15/guava-tests/test/com/google/common/collect/InternersTest.java
原理?折腾一下看看这个类的原码吧~其实实现并不难,就是折腾而已~API上是这么说的:
Interners.newWeakInterner()
Returns a new thread-safe interner which retains a weak reference to each instance it has interned, and so does not prevent these instances from being garbage-collected. This most likely does not perform as well as newStrongInterner(), but is the best alternative when the memory usage of that implementation is unacceptable. Note that unlike String.intern(), using this interner does not consume memory in the permanent generation.
这样就可以解决FULL GC问题了吧。效果如何?试试看。
厄.其实这样也会使堆产生很多String,但应该能被回收掉吧.
在jdk7下慎用String.intern()作为synchronized的对象锁的更多相关文章
- 慎用String.intern()作为synchronized的对象锁
https://www.cnblogs.com/yhlx/p/3498387.html
- synchronized是对象锁还是全局锁
昆昆欧粑粑 2019-02-20 15:09:59 1148 收藏 1分类专栏: java学习 文章标签: synchronized 全局锁 对象锁 同步版权都可以锁!synchronized(thi ...
- (转)Synchronized(对象锁)和Static Synchronized(类锁)的区别
场景:面试的时候经常用得到! 1 综述 Synchronized和Static Synchronized区别 一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全 ...
- synchronized的对象锁和类锁
概念 synchronized 是 Java 中的关键字,是利用锁的机制来实现同步的. 锁机制有如下两种特性: 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制, ...
- synchronized同步对象锁
package com.system.util; import com.common.Constants; import com.util.Cache; /** * 创建同步对象锁 * * @auth ...
- Java锁Synchronized,对象锁和类锁举例
Java的锁分为对象锁和类锁. 1. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内针对该对象的操作只能有一个线程得到执行.另一个线程必须 ...
- JAVA多线程之Synchronized关键字--对象锁的特点
一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...
- synchronized (string.intern())
在jdk7下慎用String.intern()作为synchronized的对象锁: https://www.cnblogs.com/yhlx/p/3498387.html String.intern ...
- String学习之-深入解析String#intern
引言 在 JAVA 语言中有8中基本类型和一种比较特殊的类型String.这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念.常量池就类似一个JAVA系统级别提供的缓存. 8 ...
随机推荐
- 面向对象相关概念与在python中的面向对象知识(魔法方法+反射+元类+鸭子类型)
面向对象知识 封装 封装的原理是,其成员变量代表对象的属性,方法代表这个对象的动作真正的封装是,经过深入的思考,做出良好的抽象(设计属性时用到),给出“完整且最小”的接口,并使得内部细节可以对外透明( ...
- Bootstrap常用的自带插件
Bootstrap自带的那些常用插件. 模态框 模态框的HTML代码放置的位置 务必将模态框的HTML代码放在文档的最高层级内(也就是说,尽量作为 body 标签的直接子元素),以避免其他组件影响模态 ...
- Linux 命令配置IP
配置静态IP:ip addr add 192.168.18.18/24 dev eth0 启动网卡:ifup eth0/ifup ifcfg-eth0 添加默认网关路由:ip route add de ...
- shell脚本基础和grep文本处理工具企业应用2
shell脚本编程: 编程语言的分类: 根据运行方式 编译运行:源代码-->编译器(编译)-->程序文件 优 ...
- 【javascript】[].slice.call(arguments)的作用
var thisExtends = function () { var args = [].slice.call(arguments).filter(function (item) { return ...
- android 拍照上传文件 原生定位
最近公司需要一个android拍照上传和定位功能的的单一功能页面,一开始选择ionic cordova angular的一套H5框架,但是遇到和上传文件报错的问题,bug找了一天没找到原因,怀疑是io ...
- 解决tomcat控制台乱码+清除过期缓存条目后可用空间仍不足 - 请考虑增加缓存的最大空间问题
一.乱码 1.打开Tomcat的目录,找到conf文件夹,一般修改server.xml中的编码集,改为utf-8即可 2.若server.xml中编码设置的就是utf-8,可以修改logging.pr ...
- SSM项目无法解析JSP页面
JSP页面显示标头<%@ page language="java" contentType="text/html; charset=UTF-8" page ...
- Win7 右键 新建图标消失的解决办法
方法一: 把下面一段代码存在一个记事本上,再选择另存为1.cmd,最后运行! regsvr32 /u /s igfxpph.dll reg delete HKEY_CLASSES_ROOT\Direc ...
- css3 扇形动画
扇形动画,因为我工作中遇到了只执行一次就ok,所以没细研究,该css暂时只能执行1次扇形动画,无限循环会有问题. css: @keyframes rotateAn{ 0%{transform: rot ...