String的replace導致內存溢出


從一次內存溢出來看JDK的String應該怎么用

背景

    JDK在String類中給我們提供的API,replace是個使用頻率很高的的方法。因為他可以對字符串內容進行替換,只需要輸入替換字符串和被替換字符串,就可以輕松得到你想要的字符串,功能非常強大。從JDK里的說明就能看出它有多方便了:

源碼:

    public String replace(CharSequence target, CharSequence replacement) {
        return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
             this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
    }

事故回放

   好了,接下來描述的場景純屬虛構,如有雷同,那一定是你也踩過類似的坑!
今天產品MM找你實現一個功能,要求對你們網站上的一些不友好的評論使用“嗶~”屏蔽掉,但是判斷不友好的功能比較復雜,用到了NLP,所以用到了算法同事給你提供的接口判斷,那么接下來事情就簡單了:
  我們只需要將NLP處理之后返回來的字符串作為String.replace(target, replacement)的入參target,"嗶~"作為replacement就好了。相信熟練的你很快能噼里啪啦敲出以下代碼:

      // 調用NLP接口判斷不友好的內容
        List<String> waitForReplace = callRpcNLP(text);
        if (waitForReplace == null || waitForReplace.size() == 0) {
            return;
        }
        // 逐一進行替(和)換(諧)
        for (String target : waitForReplace) {
            if (target == null) {
                continue;
            }
            text.replace(target, "嗶~");
        }

 

看起來很不錯,各種校驗也都有了,我的代碼果然寫得優美又健壯,你已經忍不住陶醉在自己的傑作中了,那么這樣有沒問題呢?

事實上,到了真正運行的時候,內存爆了!!!

案情分析

原因之一

    那么到底發生了什么,debug之下,我們定位到replace方法,發現正是因為遠程RPC接口返回了空字符串"",而空字符串作為replace的入參target時,會對任意字符串匹配多次匹配成功。我們用個簡單點的字符串來做下實驗:
String text = "hello";
System.out.println(text.replace("","*"));
打印:
嗶!h嗶!e嗶!l嗶!l嗶!o嗶!
    大家能看到,替換后的字符串出現了6個“嗶~”。也就是說,一個簡簡單單的"hello"字符串,在repalce運行之后,被""匹配出了6處需要替換的地方,那么如果不是一個"hello",而是一大段文本,一篇幾千字的論文呢?到了這里,我們距離真相已經很近了,""會導致replace方法對字符串進行多次匹配。這是內存爆了的其中一個原因。

原因之二

   至於另一個原因,就得說下replace的實現了,上面說到的字符串匹配,大家很容易想到正則表達式,實際上,replace內部也確實是通過調用正則表達式相關的API,來實現字符串匹配的。xxxxxxxxxx 原因之二至於另一個原因,就得說下replace的實現了,上面說到的字符串匹配,大家很容易想到正則表達式,實際上,replace內部也確實是通過調用正則表達式相關的API,來實現字符串匹配的。
     而正則表達式實現字符串匹配,實際上是個很復雜的操作,replace(target, replacement)中的target,在正則表達式中稱為"模式"(pattern),而匹配的過程,需要每次從字符串拿出一個字符和模式中的字符匹配。如果匹配成功,那么字符串拿出下一個字符,模式也拿出下一個字符,繼續下一輪的匹配,但是我們知道,正則表達式支持使用點“.”匹配任意字符,星號"*"匹配任意數量的字符,所以整個匹配的過程需要遞歸進行,沒法通過兩個字符串簡單地一對一移動指針來完成匹配。
總結

 

通過這次”事故“,我們知道了String.replace方法是有可能導致內存溢出的。總結下我們如何防止出現這類問題:

  • 入參target不但需要做null校驗,還要做""空字符串校驗

  • 防止直接輸入大段文本進行匹配,可通過對文本分片實現


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM