有一段这样的代码:

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的对象锁的更多相关文章

  1. 慎用String.intern()作为synchronized的对象锁

    https://www.cnblogs.com/yhlx/p/3498387.html

  2. synchronized是对象锁还是全局锁

    昆昆欧粑粑 2019-02-20 15:09:59 1148 收藏 1分类专栏: java学习 文章标签: synchronized 全局锁 对象锁 同步版权都可以锁!synchronized(thi ...

  3. (转)Synchronized(对象锁)和Static Synchronized(类锁)的区别

    场景:面试的时候经常用得到! 1 综述 Synchronized和Static Synchronized区别 一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全 ...

  4. synchronized的对象锁和类锁

    概念 synchronized 是 Java 中的关键字,是利用锁的机制来实现同步的. 锁机制有如下两种特性: 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制, ...

  5. synchronized同步对象锁

    package com.system.util; import com.common.Constants; import com.util.Cache; /** * 创建同步对象锁 * * @auth ...

  6. Java锁Synchronized,对象锁和类锁举例

    Java的锁分为对象锁和类锁. 1. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内针对该对象的操作只能有一个线程得到执行.另一个线程必须 ...

  7. JAVA多线程之Synchronized关键字--对象锁的特点

    一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...

  8. synchronized (string.intern())

    在jdk7下慎用String.intern()作为synchronized的对象锁: https://www.cnblogs.com/yhlx/p/3498387.html String.intern ...

  9. String学习之-深入解析String#intern

    引言 在 JAVA 语言中有8中基本类型和一种比较特殊的类型String.这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念.常量池就类似一个JAVA系统级别提供的缓存. 8 ...

随机推荐

  1. mybatis sql语句中 in() 长度为0或null的情况

    mybatis sql语句中 in() 长度为0或null的情况 比如: select * from A where colName IN <foreach collection="m ...

  2. 【Day2】1.循环结构

     视频地址(全部) https://edu.csdn.net/course/detail/26057 课件地址(全部) https://download.csdn.net/download/gentl ...

  3. 【wifi移植 3】开发板wifi自动获取IP

    内核版本:3.4.61 1. 配置内核,支持DHCP ~/kernel$ make menuconfig [*] Networking support  ---> Networking opti ...

  4. GridView 二维排布

    与ListView一维排布相对 public class MainActivity extends Activity implements AdapterView.OnItemClickListene ...

  5. Linux根文件系统和目录结构及bash特性3

    bash的基础特性: 命令补全:        shell程序在接收到用户执行命令的请求,分析完成之后,最左侧的字符串会被当作命令        命令查找机制:            查找内部命令   ...

  6. insightface数据裁剪过程

    数据裁剪 我们用lfw数据做实验,你也可以自己找数据. lfw数据 http://vis-www.cs.umass.edu/lfw/ 我下载的是这个原图像https://drive.google.co ...

  7. libusb 3.0

    1)usb3.0 在windows使用winusb时,出现ReadFile的input buffer 太大时,读不到数据问题? 2)linux下,usb 3.0 libusb_claim_interf ...

  8. Python模块struct(二进制数据服务)

    struct模块 Python没有专门处理字节的数据类型.但由于b'str'可以表示字节,所以,字节数组=二进制str. 而在C语言中,我们可以很方便地用struct.union来处理字节,以及字节和 ...

  9. Gym - 101955E The Kouga Ninja Scrolls (曼哈顿距离变换+线段树)

    题意:有n个忍者(编号为1-n),每个忍者有三个属性:横坐标x,纵坐标y,所属门派c,要求支持三种操作: 1.改变第k个忍者的位置 2.改变第k个忍者的门派 3.查询编号为[l,r]之间的忍者中,所属 ...

  10. yii学习笔记(四)

    return $this->goBack(); // 先看看Yii::$app->user->returnUrl是否已经设置, returnUrl没有设置且goBack()中的参数也 ...