首页 > 解决方案 > 寻找一种 GC 友好的方式来频繁替换子字符串

问题描述

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

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

但是我使用 trie 树和 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()返回结果,这个操作会创建一个新的字符串对象。同时结果的生命周期很短,替换函数的调用很频繁。结果JVM会频繁GC。

有什么方法可以重用短寿命结果字符串占用的内存?或者只是其他一些解决方案。

标签: androidstringkotlingarbage-collectionstringbuilder

解决方案


有什么方法可以重用短寿命结果字符串占用的内存?

不。

或者只是其他一些解决方案。

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

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

另一个问题是调用者实际上每次都会获得具有不同状态的相同对象。它无法保持状态……除非它要求toString()它。


但是您可能会不必要地担心性能。GC 比较擅长处理短生命周期的对象。假设一个对象在创建后的第一个 GC 循环中是不可访问的,它永远不会被标记或复制,删除它的成本将为零。大致而言,“从”空间中的可到达对象将花费您。

我会先做一些分析和 GC 监控。如果有明确的证据表明短暂的字符串导致性能问题,请仅按照上述更改代码的方式进行。

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


推荐阅读