Java字符串interning的替代方案

8

由于Java默认的字符串池机制备受批评,我正在寻找替代方案。

您能否建议一个API作为Java字符串池机制的良好替代品?我的应用程序使用Java 6。我的主要需求是通过字符串池机制避免重复字符串。

有关不良消息:

  • 字符串池机制是通过本地方法实现的。C语言实现使用了固定大小的约1000个条目,并且对大量字符串的处理效率很低。
  • Java 6在Perm gen区域存储了字符串池中的字符串。因此,它们不会被垃圾回收并可能导致perm gen错误。我知道这在Java 7中已经修复,但我不能升级到Java 7。

为什么需要使用字符串池机制?

  • 我的应用程序是一个服务器应用程序,堆大小为10-20G,针对不同的部署。
  • 在分析过程中,我们发现数十万个字符串是重复的,通过避免存储重复的字符串,我们可以显着提高内存使用率。
  • 内存一直是我们的瓶颈,因此我们针对其进行了优化,而不是进行任何过早的优化。

3
我会尽力进行翻译。以下是需要翻译的内容:Part of me respects the requirements you post, but if "bad press" is enough for you to avoid them, then I really do have to ask how you've profiled your application (if at all) to determine Java strings are not suitable.我一部分尊重你发布的要求,但如果“负面报道”足以让你避开它们,那么我真的必须问问你是否对你的应用程序进行了分析(如果有的话),来确定Java字符串不适合使用。 - djechlin
1
你有没有注意到你的应用程序在这些问题方面存在问题?如果没有,我就不会担心它。 - Keppil
如果我使用Set,我将能够检查该字符串是否重复,但将无法获取对原始字符串的引用,以便我不使用重复的字符串。 - MoveFast
1
@ManojGumber https://dev59.com/K1_Va4cB1Zd3GeqPRkEr (使用Map实现),https://dev59.com/zVHTa4cB1Zd3GeqPP0K4(提到了Guava Interner)。 - user166390
@pst Guava的内部缓存看起来很有前途。 - MoveFast
显示剩余4条评论
1个回答

12

String intern方法是通过本地方法实现的。C语言实现使用了一个固定大小为1k的条目,并且对于大量字符串的情况缩放非常差。

当存在许多千个字符串时,它的缩放效果很差。

Java 6将interned字符串存储在Perm gen中。因此不会被GC清除。

只有当Perm gen被清理时,它才会被清除,但这并不经常发生,如果您不增加它,可能会达到此空间的最大值。

我的应用程序是具有10-20G堆大小的服务器应用程序,适用于不同的部署。

我建议您考虑使用离堆内存。在一个应用程序中,我有500 GB的离堆内存和约1 GB的堆。尽管它并不适用于所有情况,但值得考虑。

在分析过程中,我们发现数十万个字符串都是重复的,通过避免存储重复的字符串,我们可以显著提高内存使用率。

为此,我使用了一个简单的String数组。这非常轻量级,您可以轻松控制存储的字符串上限。


这是一个通用的内部器示例。

class Interner<T> {
    private final T[] cache;

    @SuppressWarnings("unchecked")
    public Interner(int primeSize) {
        cache = (T[]) new Object[primeSize];
    }

    public T intern(T t) {
        int hash = Math.abs(t.hashCode() % cache.length);
        T t2 = cache[hash];
        if (t2 != null && t.equals(t2))
            return t2;
        cache[hash] = t;
        return t;
    }
}

这个缓存的一个有趣特性是它不需要线程安全。
为了提高速度,你可以使用2的幂次方作为大小和位掩码,但这更加复杂,并且根据哈希码的计算方式可能无法很好地工作。

对于字符串数组的方法,它只是一个无序集合吗? - user166390
@peter Lawrey,它将如何处理冲突?即当具有不同哈希码的两个字符串指向相同的缓存索引时会发生什么?您是否假设Intern的大小与您预期的不同字符串数量相同? - MoveFast
如果发生冲突,它将替换那里的值。大小需要比您认为最优的字符串数量大2-3倍,因为它不会尝试非常聪明地处理冲突。顺便说一下,即使HashMap也将是条目数量的1.4到2.8倍。您可以使用http://primes.utm.edu/curios/找到任何大小的“有趣”素数。 - Peter Lawrey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接