在路上

 找回密码
 立即注册
在路上 站点首页 学习 查看内容

jvm之String常量池的优化

2016-12-13 12:56| 发布者: zhangjf| 查看: 560| 评论: 0

摘要: 原文 http://wangxinchun.iteye.com/blog/2190841 String.intern() 方法会自动把String放到jvm的PSPermGen的常量区。 关于String.intern()的使用需要注意以下两点: 1、对于日常工作中List中的数据对象, ...
原文 http://wangxinchun.iteye.com/blog/2190841

String.intern() 方法会自动把String放到jvm的PSPermGen的常量区。

关于String.intern()的使用需要注意以下两点:

1、对于日常工作中List中的数据对象,如果对象的某个属性是String,比如性别,国家等重复率较高的字符串取值,如果放入常量区,会节省大量的内存空间。

2、jvm的常量池的的搜索比较慢,速度甚至比ConcurrentHashMap 慢了不少。

证明:

  1. public static long times = 10000000L;
  2. public static void testIntern() {
  3. System.gc();
  4. List<String> list = new ArrayList<String>();
  5. long l = System.currentTimeMillis();
  6. for (int i = 0; i < times; i++) {
  7. list.add(("A" + (i % 1000)).intern());
  8. }
  9. long ll = System.currentTimeMillis();
  10. System.out.println("testIntern time :" + (ll -l));
  11. System.gc();
  12. System.out.println("testIntern:"
  13. + (wrapM(Runtime.getRuntime().totalMemory()) - wrapM(Runtime
  14. .getRuntime().freeMemory())));
  15. }
复制代码


结果:

testIntern time :2657

testIntern memory:1.8076171875M

eden space 86272K, 3% used [0x00000000f9c00000,0x00000000f9f4b3f0,0x00000000ff040000)

  1. public static void testCommon() {
  2. System.gc();
  3. List<String> list = new ArrayList<String>();
  4. long l = System.currentTimeMillis();
  5. for (int i = 0; i < times; i++) {
  6. list.add(("A" + (i % 1000)));
  7. }
  8. long ll = System.currentTimeMillis();
  9. System.out.println("testIntern time :" + (ll -l));
  10. System.gc();
  11. System.out.println("testCommon memory:"
  12. + (wrapM(Runtime.getRuntime().totalMemory()) - wrapM(Runtime
  13. .getRuntime().freeMemory())));
  14. }
复制代码

结果:

Exception in thread "main" [Full GC [PSYoungGen: 81920K->0K(92160K)] [PSOldGen: 102400K->188K(102400K)] 184320K->188K(194560K) [PSPermGen: 3048K->3048K(21248K)], 0.0530156 secs] [Times: user=0.05 sys=0.00, real=0.05 secs]

java.lang.OutOfMemoryError: GC overhead limit exceeded

at java.lang.StringBuilder.toString(StringBuilder.java:430)

at com.mystore.core.common.TestMemory.testCommon(TestMemory.java:85)

at com.mystore.core.common.TestMemory.main(TestMemory.java:13)

  1. public static void testCurrentHashMap() {
  2. System.gc();
  3. List<String> list = new ArrayList<String>();
  4. long l = System.currentTimeMillis();
  5. for (int i = 0; i < times; i++) {
  6. list.add((StringCache.get("A" + (i % 1000))));
  7. }
  8. long ll = System.currentTimeMillis();
  9. System.out.println("testIntern time :" + (ll -l));
  10. System.gc();
  11. System.out.println("testCurrentHashMap memory:"
  12. + (wrapM(Runtime.getRuntime().totalMemory()) - wrapM(Runtime
  13. .getRuntime().freeMemory())));
  14. }
  15. private static double wrapM(long length) {
  16. return length / 1024 / 1024.0;
  17. }
  18. static class StringCache {
  19. private static ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>(10000);
  20. public static String get(String str){
  21. if(null == str){
  22. return null;
  23. }
  24. String ret = map.get(str);
  25. if(null == ret){
  26. map.putIfAbsent(str, str);
  27. ret = map.get(str);
  28. }
  29. return ret;
  30. }
  31. }
复制代码

结果:testIntern time :2006

testCurrentHashMap memory:1.9482421875

eden space 85888K, 3% used [0x00000000f9c00000,0x00000000f9f477f8,0x00000000fefe0000)

结论:

对比testCommon 和 testIntern 说明testCommon 会占用较多的堆区内存,testIntern 会导致常量区会有微量的增长(仅仅1000个字符常量而已)

对比testIntern 和 testCurrentHashMap ,testCurrentHashMap 在性能方面有优势,更为需要关注的是testCurrentHashMap的内存分配在了堆区,而testIntern 分配在了常量区,一般情况下 堆区的老年代要比持久代要大的多,所以从gc的角度来说,更应该使用testCurrentHashMap 的方式。不好的一点是 testCurrentHashMap 中的常量会一直增长没有过期 策略,而常量池则会在full gc 的时候自动做清理。testCurrentHashMap 优化的方向是 使用带缓存并且线程安全的Map,比如guava的缓存Map

参考: 性能对比参见 http://stackoverflow.com/questio ... ty-of-string-intern

最新评论

小黑屋|在路上 ( 蜀ICP备15035742号-1 

;

GMT+8, 2025-7-7 06:36

Copyright 2015-2025 djqfx

返回顶部