• 技术文章 >后端开发 >PHP问题

    小小一个改动,竟能让json_decode内存节省70%?

    醉折花枝作酒筹醉折花枝作酒筹2021-07-22 17:41:49转载92
    json_decode对JSON格式的字符串进行解码,接受一个JSON格式的字符串并且把它转换为PHP变量。但是运行json_decode可能会出现超出内存上线,这是该怎么办?

    如果你用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.4M8.7M
    平均json_decode解析时间0.73s0.41s

    解析100万个MAC:

    保存MAC地址方式数组方式字符串方式
    JSON文件大小20M18M
    平均内存占用204.6M54.2M
    平均json_decode解析时间1.61s0.81s

    解析200万个MAC:

    保存MAC地址方式数组方式字符串方式
    JSON文件大小40M36M
    平均内存占用409.0M108.2M
    平均json_decode解析时间3.05s1.53s

    推荐学习:php视频教程

    以上就是小小一个改动,竟能让json_decode内存节省70%?的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:CSDN,如有侵犯,请联系admin@php.cn删除
    专题推荐:PHP
    上一篇:如何用box/spout解析大型Excel表格 下一篇:php怎么利用foreach删除数组元素
    第16期线上培训班

    相关文章推荐

    • php设置http状态码的方法• php怎么实现色彩空间转换• macbook pro怎么安装php• php rename错误原因怎么找• PHP如何读写protobuf3

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网