寻找一种 GC 友好的方式来频繁替换子字符串

Look for a GC friendly way to replace substring frequently

我的目标是简单地替换子字符串,但是非常频繁。程序运行在Android.

比如我有一个字符串={a} is a good {b}.和一个映射={{a}=Bob, {b}=boy},结果应该是Bob is a good boy.我需要处理最多400个不同字符串的替换times peer second 因为map的值会实时更新。

但是我使用特里树和 Aho-Corasick 自动机来获得高性能,这里是核心片段:

    val builder: StringBuilder

    private fun replace(str: String): String {
        if (!getFail) {
            getFail()
        }
        var p = 1
        builder.setLength(0)
        for (c in str) {
            builder.append(c)
            if (c.toInt() !in 0..126) {
                continue // ignore non-ascii char
            }
            var k = trie[p][c.toInt()]
            while (k > 1) {
                // find a tag
                if (end[k] != 0) {
                    val last = builder.length - end[k]
                    // replace the tag
                    values[builder.sub(last, end[k])]?.let {
                        builder.replace1(last, end[k], it)
                    }
                    p = 0
                    break
                }
                k = fail[k] // not find
            }
            p = trie[p][c.toInt()]
        }
        return builder.toString()
    }

如您所见,我已经使用 StringBuilder 来重用内存,但最后我不得不调用 StringBuilder.toString() 来 return 结果,此操作创建了一个新的字符串对象。同时result的生命周期很短,替换函数调用非常频繁。因此JVM会频繁GC。

有什么办法可以重用short life result string占用的内存吗?或者只是其他一些解决方案。

Any way to reuse the memory occupied by the short life result string?

没有

Or just some other solution.

如果您可以更改使用此方法生成的 String 对象的代码以接受 CharSequence。然后你可以将它传递给 builder 中的 StringBuilder 实例,并避免 toString() 调用。

问题是您无法阻止某些东西将 CharSequence 转换为 StringBuilder 并对其进行变异。 (但如果代码不是安全关键的,你可以忽略它。意外地这样做很难,特别是如果你在传递 StringBuilder 时使用 CharSequence 接口类型。)

另一个问题是调用者实际上每次都会获得相同的对象,但状态不同。它无法保持状态...除非它在其上调用 toString()


但是您可能不必要地担心性能。 GC比较擅长处理寿命短的对象。假设一个对象在创建后的第一个 GC 周期是不可访问的,它永远不会被标记或复制,并且删除它的成本将为零。初步估计,"from" space 中的可达对象将花费您。

我会先做一些分析和 GC 监控。只有在有明确证据表明生命周期较短的字符串 导致性能问题时,才按照上述方式更改代码。

(我的直觉是每秒 400 个短期字符串应该不是问题,假设 1)它们不是很大并且 2)你选择了一个适合你的用例的 GC。)