首頁  >  文章  >  後端開發  >  小小一個改動,竟能讓json_decode記憶體節省70%?

小小一個改動,竟能讓json_decode記憶體節省70%?

醉折花枝作酒筹
醉折花枝作酒筹轉載
2021-07-22 17:41:491856瀏覽

json_decode對JSON格式的字串進行解碼,接受一個JSON格式的字串並且把它轉換為PHP變數。但是運行json_decode可能會出現超出記憶體上線,這是該怎麼辦?

小小一個改動,竟能讓json_decode記憶體節省70%?

如果你用PHP的json_decode函數解析JSON字串,而且JSON字串中包含一個大量元素的數組,那你就要小心解析過程中PHP超出記憶體上限了。

筆者在開發時遇到一個JSON檔案需要解析,該JSON中包含了一個由眾多MAC位址組成的數組,像這樣:

{
    "name": "MAC File",
    "date": "2017-11-08",
    "macList": [
        "11-11-11-11-11-11",
        "22-22-22-22-22-22",
        ...
    ]
}

結果json_decode過程中超出了PHP默認的128M內存上限。

WHAT,超限了? !這張JSON檔才 10M 啊!

在大罵「這函數是不是有BUG」之後,仔細推敲下,發現問題出在MAC位址構成的陣列上。要知道PHP的陣列是很吃記憶體的。

PHP數組究竟多吃內存,可以做個簡單的試驗,向數組內放入50萬個MAC地址,打印內存佔用量:

$a = [];
for ($i = 0; $i !== 500000; $i++) {
    $a[] = '11-11-11-11-11-11';
}
echo memory_get_usage() . PHP_EOL;

如果把這些MAC地址寫在一個文件裡,理論上只佔9.6M 磁碟空間,但是PHP數組維護相同的訊息,卻佔了72.4M 記憶體。

有辦法解決json_deocde時的記憶體超限嗎?當然,簡單粗暴點的話就把記憶體上限調高:

ini_set('memory_limit','1024M');

雖然可行,但會引發一個問題,就是:

可能被其他語言的工程師嘲笑PHP佔內存。

那有沒有更明智的辦法解決記憶體超限問題?

有。因為PHP數組佔用記憶體很大,所以我們要避免json_decode在解碼時產生巨大數組。怎麼做呢?這就要從JSON的編碼格式下手,例如可以把巨大的JSON數組修改成字串:

{
    "name": "MAC File",
    "date": "2017-11-08",
    "macList": "11-11-11-11-11-11,22-22-22-22-22-22,...",
}

我把macList從數組轉換成以逗號分割的字串。這樣可以避免json_decde產生一個巨大無比的數組,取而代之的是一個超長的字串。

字串佔用的記憶體量可比陣列小多了,剛才50萬個MAC位址,瞬間只佔9.7M的記憶體。修改完之後,json_decode順利解析成功,而且解析速度更快了。

原先macList是個數組,可以透過foreach遍歷其中的元素,現在是字串了,該怎麼遍歷呢?

不難,可以用strtok:

$tok = strtok($macList, ',');
while ($tok !== false) {
    $mac = $tok;
    $tok = strtok(',');
}

遍歷的難度沒有增加多少,對吧?

你可能會問,這種方法可以對付簡單的JSON數組,如果一個JSON數組的每個元素是個JSON對象,該怎麼辦?

我們可以這樣建構字串:

{
    "list": '{"name":"obj1"}###{"name":"obj2"}###...'
}

字串由一個小的JSON組成,它們之間用特殊的標記

分割。在解析時,根據特殊標記切分出一個個JSON對象,再用json_decode逐個解析:

$tok = strtok($objectList, '###'); // 按###切割
while ($tok !== false)
{
    $objectStr = $tok;
     // 每切割出一个JSON对象就解码
    $object = json_decode($objectStr, true);
    $tok = strtok('###');
}

你也可以自創一種編碼/解析這個超長字串的方法,總之最終目標就是避免json_decode在解碼過程中產生超大數組。

透過本文,你應該略到了PHP Array吃記憶體的能力。把JSON中的陣列替換成字串表示,可以節省大量的記憶體。我還跑了一個比較數據,給大家參考:

解析50萬個MAC:保存MAC位址方式數組方式字串方式JSON檔案大小9.6M8.6M(每個元素省了一對引號)平均記憶體佔用72.4M#8.7M平均json_decode解析時間0.73s0.41s

解析100萬個MAC:#儲存MAC位址方式陣列方式字串方式JSON檔案大小20M18M平均記憶體佔用204.6M#54.2M#平均json_decode解析時間
##1.61s 0.81s

解析200萬個MAC:

儲存MAC位址方式 陣列方式 字串方式
JSON檔案大小 40M 36M
#平均記憶體佔用 409.0M 108.2M
平均json_decode解析時間 3.05s 1.53s
##推薦學習:

php影片教學

以上是小小一個改動,竟能讓json_decode記憶體節省70%?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除