Java 求解答:为什么 JackSon 和 fastJson 里使用 string.intern,欢迎美团技术团队解答:-) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
badboy17
V2EX    Java

Java 求解答:为什么 JackSon 和 fastJson 里使用 string.intern,欢迎美团技术团队解答:-)

  •  
  •   badboy17 2022 年 8 月 8 日 4170 次点击
    这是一个创建于 1356 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题:使用 string.intern 可以节省时间吗,可以节省空间吗?

    前段时间,迁移容器化之后发现机器的 youngGc 时间缓慢上涨,从几十毫秒涨到了几百毫秒,然后出发 full gc 之后,youngGc 时间又重新回落到几十毫秒 后来确定了是系统产生了很多的不同字符串的 key ,同时 jackSon 的 internCache 调用了 string.intern()方法,导致了字符串常量池 stringTable 膨胀,最终导致 YoungGc 扫描变慢

    看了下美团技术团队里写道,fastJson 里使用了 string.intern,可以大大减少时间和空间,不太理解为什么可以节省时间,节省空间我能够理解,但是使用 string.intern ,不是更加的耗时吗?

    https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html

    22 条回复    2022-08-09 17:03:49 +08:00
    urnoob
        1
    urnoob  
       2022 年 8 月 8 日 via Android
    你先了解下 intern 。
    你的情况的慢是因为短时间内产生了大量不同的字符串进入了 table 。
    美团说的是字符串 xxxx 在进入 table 后,以后相同字符串都不只产生一个对 table 内 xxxx 的引用,也就是说只有一个真正的字符串。内存节省了,gc 也快了。
    sutra
        2
    sutra  
       2022 年 8 月 8 日
    美团那个文章不是有一节“####3,fastjson 不当使用 String#intern”?
    badboy17
        3
    badboy17  
    OP
       2022 年 8 月 8 日
    @sutra 我们遇到的就是这样的问题,接口传来了不同 Key 的 json ,反序列化导致了 stringTable 一直缓慢的膨胀
    badboy17
        4
    badboy17  
    OP
       2022 年 8 月 8 日
    @urnoob gc 为什么一定会变快呢,入参穿进来的字符串一定是一个新的字符串啊,一定需要被 gc 掉,如果使用 return string.intern()的话,这个入参会被 gc 掉,如果直接返回这个入参字符串的话,就相当于多占用了一份空间,在未来的某个时间可能会被 gc ,但是这两种情况都会有一个相同的垃圾需要被 gc ,也就是入参传进来的那个字符串
    badboy17
        5
    badboy17  
    OP
       2022 年 8 月 8 日
    @urnoob 可能是我写的顺序有误,其实只是想说下之前遇到这个问题的背景,背景跟问题无关,我的意识并不是说为什么我用了 intern string 变慢了,但是官方说 string.intern 可以减少时间,我的问题只是想问,为什么 intern.string 作为缓存,减少耗时,变快
    Macolor21
        6
    Macolor21  
       2022 年 8 月 8 日
    @1023363777 #4 入参的对象只是一个字符串常量池的引用,intern 加速 GC 的含义也是这个,就算有一样的 String 对象,引用的也是这个常量池的地址。这样避免真实 String 对象(数组)被频繁创建、回收。从而达到缓存的目的,由于都是引用对象,因此 GC 非常快
    Macolor21
        7
    Macolor21  
       2022 年 8 月 8 日
    还有 intern 耗时的问题,如果常量池已经有了,那么就会直接返回这个常量池对象的地址,不会执行放入常量池的操作

    这个看代码注释就有,对于你的场景,太多的新的字符串对象被放入常量池,那么空间和时间效率都比较低
    kkkiio
        8
    kkkiio  
       2022 年 8 月 8 日
    少分配,cache 命中高,对 object mapping 那种场景比较好,对 key 范围大的不好
    kkkiio
        9
    kkkiio  
       2022 年 8 月 8 日
    @kkkiio 我记得 HashMap 比较 Key 时会先比较 ref (地址),也是 intern 的优化场景
    badboy17
        10
    badboy17  
    OP
       2022 年 8 月 8 日
    @Macolor21 耗时相比起直接就返回入参的字符串,难道不是一定会更慢吗,但是节省了空间是一定的
    badboy17
        11
    badboy17  
    OP
       2022 年 8 月 8 日
    @Macolor21
    这是 JackSon 的 intenCache 的源码,为什么入参的 input 你认为一定是一个字符串常量池的引用呢,如果假设现在的 json 串的 Key 是字符串“name”,我认为执行这个方法的入参的这个字符串 Input 对象,一定是一个新的,在堆区分配空间的字符串对象
    public String intern(String input) {
    String result = (String)this.get(input);
    if (result != null) {
    return result;
    } else {
    if (this.size() >= 180) {
    synchronized(this.lock) {
    if (this.size() >= 180) {
    this.clear();
    }
    }
    }

    result = input.intern();
    this.put(result, result);
    return result;
    }
    }
    zjp
        12
    zjp  
       2022 年 8 月 8 日
    Jackson 的 InternCache 默认只存 180 条,确定是这个导致的吗?
    badboy17
        13
    badboy17  
    OP
       2022 年 8 月 8 日
    @zjp 不是这个,是 string.intern()方法,导致字符串常量池里的 stringTable 缓存了过多的字符串,增加了 yougGC 的扫描耗时
    zjp
        14
    zjp  
       2022 年 8 月 8 日
    #13 发现看不懂这个 Cache 了...都必须要调用 string.intern()
    urnoob
        15
    urnoob  
       2022 年 8 月 8 日 via Android
    @badboy17
    参考这段 里面多了个 不 字我修正下
    字符串 xxxx 在进入 table 后,以后相同字符串都只产生一个对 table 内 xxxx 的引用,也就是说只有一个真正的字符串。
    虽然不太严谨,但是扫描和回收 10 个对象肯定比 10 引用慢。更何况这些引用可能绝大部分都是在方法内,在栈上,方法退出就释放了 都不涉及 gc
    Macolor21
        16
    Macolor21  
       2022 年 8 月 8 日
    @badboy17 #11 第三行这个 return result; 不是吗? JDK 内部也有类似的实现,不过是 native 方法。这个 input 对象,第二次进来就是常量池的内存地址了,因为语言级别上已经也有这个机制
    cubecube
        17
    cubecube  
       2022 年 8 月 9 日
    JDK8 上,String.intern 如果数据多了之后,查找非常慢,有非常大的性能问题。
    L0L
        18
    L0L  
       2022 年 8 月 9 日
    从现象上看,突然有两个疑问点:
    1 、YoungGC 变慢是扫描了全部的字符串常量池 ?
    2 、YoungGC 无法回收字符串常量池里的 stringTable 缓存的字符串 ?
    zmal
        19
    zmal  
       2022 年 8 月 9 日
    当 stringtable 里的字符串被复用的时候就节省时间了呗,intern() 不就是做这个的嘛。
    zmal
        20
    zmal  
       2022 年 8 月 9 日
    对你说的 ygc 变慢是 intern()引起的说法存疑。
    badboy17
        21
    badboy17  
    OP
       2022 年 8 月 9 日
    @zmal 这个问题很多人遇到过
    badboy17
        22
    badboy17  
    OP
       2022 年 8 月 9 日
    @L0L FullGc 会回收,但是 yougGc 不会,YoungGc 的确需要扫描字符串常量池
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2740 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 53ms UTC 10:01 PVG 18:01 LAX 03:01 JFK 06:01
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86