专栏首页芋道源码1024注册中心 Eureka 源码解析 —— StringCache

注册中心 Eureka 源码解析 —— StringCache

  • 1. 概述
  • 2. StringCache
  • 3. 使用场景

1. 概述

本文主要分享 Eureka 自己实现的 StringCache

先一起来看下美团点评技术团队对 String#intern(...) 的分享:

FROM 《深入解析String#intern》「 引言 」 在 JAVA 语言中有8中基本类型和一种比较特殊的类型 String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个 JAVA 系统级别提供的缓存。 8 种基本类型的常量池都是系统协调的,String 类型的常量池比较特殊。它的主要使用方法有两种:

  • 直接使用双引号声明出来的 String 对象会直接存储在常量池中
  • 如果不是用双引号声明的 String 对象,可以使用String提供的 intern 方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中
  • 字符串常量池能带来速度更快,更节省内存的好处
  • 非双引号声明的 String 对象,需要使用 String#intern() 方法,将字符串存储到字符串常量池。

看起来一切都非常非常非常美好,那为什么 Eureka 自己实现了 StringCache ?

继续参见美团点评技术团队对 String#intern(...) 的分享:

FROM 《深入解析String#intern》「 native 代码 」 JAVA 使用 JNI 调用 c++ 实现的 StringTable 的 intern 方法, StringTable的 intern 方法跟 Java 中的 HashMap 的实现是差不多的, 只是不能自动扩容。默认大小是1009。 要注意的是,String 的 String Pool 是一个固定大小的 Hashtable,默认值大小长度是 1009,如果放进 String Pool 的 String 非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降(因为要一个一个找)。 在 JDK6 中 StringTable 是固定的,就是 1009 的长度,所以如果常量池中的字符串过多就会导致效率下降很快。在jdk7中,StringTable的长度可以通过一个参数指定:

  • -XX:StringTableSize=99991
  • JDK 自带的 String Pool 固定大小( 即使可配 ),不支持自动扩容,大量使用 String#intern(…) 后,会导致性能大幅度下降。
  • Eureka 的应用实例( InstanceInfo ) 的 appNameappGroupNamevipAddresssecureVipAddressmetadata 和应用( Application )的 name 等属性需要使用到 String Pool ,为了在大量的网络通信序列化反序列的过程中,速度更快,更节省内容。

另外,FastJSON 在 1.124 版本之前也使用 String#intern(...) 方法,优化 JSON Key 的速度和空间,但是在大量动态 JSON Key 的场景下,反而会导致性能下降。所以 FastJSON 1.124 修复了该问题。参见如下:

FROM 《深入解析String#intern》「 fastjson 不当使用 」

  • But ,FastJSON 1.124 版本之前恰好适合 Eureka ,因为 appNameappGroupName 相对不那么动态。考虑到可能还是有大量的字符串存在,因而实现自定义的 StringCache 类,以解决 StringPool 的 HashTable 不支持动态扩容的情况。

OK,下面我们来看看 Eureka 是如何实现自定义的 StringCache 类。

推荐 Spring Cloud 书籍

  • 请支持正版。下载盗版,等于主动编写低级 BUG
  • 程序猿DD —— 《Spring Cloud微服务实战》
  • 周立 —— 《Spring Cloud与Docker微服务架构实战》
  • 两书齐买,京东包邮。

推荐 Spring Cloud 视频

  • Java 微服务实践 - Spring Boot
  • Java 微服务实践 - Spring Cloud
  • Java 微服务实践 - Spring Boot / Spring Cloud

2. StringCache

com.netflix.discovery.util.StringCache ,字符串缓存。代码如下:

  1: public class StringCache {
  2: 
  3:     public static final int LENGTH_LIMIT = 38;
  4: 
  5:     private static final StringCache INSTANCE = new StringCache();
  6: 
  7:     private final ReadWriteLock lock = new ReentrantReadWriteLock();
  8:     private final Map<String, WeakReference<String>> cache = new WeakHashMap<String, WeakReference<String>>();
  9:     private final int lengthLimit;
 10: 
 11:     public StringCache() {
 12:         this(LENGTH_LIMIT);
 13:     }
 14: 
 15:     public StringCache(int lengthLimit) {
 16:         this.lengthLimit = lengthLimit;
 17:     }
 18: 
 19:     public String cachedValueOf(final String str) {
 20:         if (str != null && (lengthLimit < 0 || str.length() <= lengthLimit)) {
 21:             // Return value from cache if available
 22:             try {
 23:                 lock.readLock().lock();
 24:                 WeakReference<String> ref = cache.get(str);
 25:                 if (ref != null) {
 26:                     return ref.get();
 27:                 }
 28:             } finally {
 29:                 lock.readLock().unlock();
 30:             }
 31: 
 32:             // Update cache with new content
 33:             try {
 34:                 lock.writeLock().lock();
 35:                 WeakReference<String> ref = cache.get(str);
 36:                 if (ref != null) {
 37:                     return ref.get();
 38:                 }
 39:                 cache.put(str, new WeakReference<>(str));
 40:             } finally {
 41:                 lock.writeLock().unlock();
 42:             }
 43:             return str;
 44:         }
 45:         return str;
 46:     }
 47: 
 48:     public int size() {
 49:         try {
 50:             lock.readLock().lock();
 51:             return cache.size();
 52:         } finally {
 53:             lock.readLock().unlock();
 54:         }
 55:     }
 56: 
 57:     public static String intern(String original) {
 58:         return INSTANCE.cachedValueOf(original);
 59:     }
 60: 
 61: }
  • INSTANCE 属性,字符串缓存单例
  • lock 属性,读写锁,保证读写互斥。
  • cache 属性,缓存哈希表。
    • 使用 WeakHashMap,当 StringCache 被回收时,其对应的值一起被移除。
    • 《WeakHashMap和HashMap的区别》
    • 《Java 集合系列13之 WeakHashMap详细介绍(源码解析)和使用示例》
  • lengthLimit 属性,缓存字符串最大长度。默认值:38 。
  • #cachedValueOf(…) 方法,获得字符串缓存。若缓存不存在,则进行缓存。和 String#intern() 的逻辑相同,区别在于 cache 支持自动扩容。
    • 第 22 至 30 行 :读锁,读取缓存。
    • 第 32 至 42 行 :缓存不存在,写锁,写入缓存。
  • #size() 方法,缓存大小。
  • #intern() 静态方法,使用 INSTANCE 获取缓存字符串。

3. 使用场景

在 InstanceInfo 下的使用,点击 链接 查看。

在 Application 下的使用,点击 链接 查看。

本文分享自微信公众号 - 芋道源码(javayuanma),作者:老艿艿

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-08-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 注册中心 Eureka 源码解析 —— EndPoint 与 解析器

    目前有多种 Eureka-Server 访问地址的配置方式,本文只分享 Eureka 1.x 的配置,不包含 Eureka 1.x 对 Eureka 2.x 的...

    芋道源码
  • Java 程序员排行前 10 的错误,你有没有中?

    Arrays.asList()会返回一个ArrayList,但是要特别注意,这个ArrayList是Arrays类的静态内部类,并不是java.util.Arr...

    芋道源码
  • 用 ArrayList 还是 LinkedList?

    ArrayList 和 LinkedList 是 Java 集合框架中用来存储对象引用列表的两个类。ArrayList 和 LinkedList 都实现 Lis...

    芋道源码
  • Java String 对象,你真的了解了吗?

    String对象是 Java 中使用最频繁的对象之一,所以 Java 公司也在不断的对String对象的实现进行优化,以便提升String对象的性能,看下面这张...

    平头哥的技术博文
  • httpclient爬虫爬取汉字拼音等信息

    下面是使用httpclient爬虫爬取某个网站的汉字相关信息的实践代码,中间遇到了一些字符格式的问题。之前被同事见过用html解析类来抓取页面信息,而不是像我现...

    八音弦
  • Java基础系列2:深入理解String类

    String是Java中最为常用的数据类型之一,也是面试中比较常被问到的基础知识点,本篇就聊聊Java中的String。主要包括如下的五个内容:

    王金龙
  • JVM 《四 JVM 中的String》

    String 这样的量,在我们的认知中是比较特别的。 其中String 是个对象,然后String也可以是个普通的字面量。在每代JDK中对String 的处理...

    邹志全
  • String Stringbuilder Stringbuffer异同

    字符串在编程中使用的非常频繁,同时又是面试中的常见题型,那么我们的对字符串相关类String,StringBuilder,StringBuffer的理解真的正确...

    呼延十
  • Spark Tips3: 在Spark Streaming job中读取Kafka messages及其offsetRange

    在Spark Streaming job中读取Kafka topic(s)中的messages时,有时我们会需要同步记录下每次读取的messages的offse...

    叶锦鲤
  • C#中的Lambda表达式

    在看C#中的Lambda表达式之前,先来看一个概念:匿名方法。学过Java的亲们,可能对这个概念比较了解。Java JDK8中也引入了Lambda表达式。

    卡尔曼和玻尔兹曼谁曼

扫码关注云+社区

领取腾讯云代金券