美文网首页让前端飞
记一次nodejs内存溢出、前端页面崩溃

记一次nodejs内存溢出、前端页面崩溃

作者: 小遁哥 | 来源:发表于2023-01-03 16:02 被阅读0次

    受到世界杯的影响,我想写一个程序,可以手动录入一场比赛所有盘口的赔率,胜平负3种情况、让球胜平负3种情况、比分26种情况、进球数8种情况、半全场9种情况,有的场次不能单买胜平负、让球胜平负,或者直接不开这种盘口,但相互之间串在一起不受限制。

    以A、B、C、D为场次的标识,默认一种情况投入10块钱,算出能够赢的钱再排序。当只有一场比赛的时候,很好算。当有A、B两场时,场次本身的结果有A、B、AB,当有A、B、C、三场的时候,场次本身组合的结果就有A、B、C、AB、AC、BC、ABC。

    首先要有得出涉及场次所有组合的算法,其中的规律在于,当处理的场次序号时i,i本身是一种组合,先把i之前的组合和i结合一遍,再把i放进去,依次类推,直到数组的最后一项,那么算法如下。

    const teamList = ["A", "B", "C", "D"];
    let teamCombinationList = [];
    getTeamCombinationList(0);
    function getTeamCombinationList(index) {
      const list = [];
      teamCombinationList.forEach((el) => {
        list.push([...el, teamList[index]]);
      });
      list.push([teamList[index]]);
      teamCombinationList = [...teamCombinationList, ...list];
      if (index < teamList.length - 1) {
        getTeamCombinationList(index + 1);
      }
    }
    

    结果如下

    [
      [ 'A' ],
      [ 'A', 'B' ],
      [ 'B' ],
      [ 'A', 'C' ],
      [ 'A', 'B', 'C' ],
      [ 'B', 'C' ],
      [ 'C' ],
      [ 'A', 'D' ],
      [ 'A', 'B', 'D' ],
      [ 'B', 'D' ],
      [ 'A', 'C', 'D' ],
      [ 'A', 'B', 'C', 'D' ],
      [ 'B', 'C', 'D' ],
      [ 'C', 'D' ],
      [ 'D' ]
    ]
    

    此时只需遍历teamCombinationList,有一个就是单场、有两个就是2串1、有三个就是3穿1,因为场次的个数不确定,现在需要一个通用算法实现类似多层for循环的结果。因为每个场次最多有51种结果,以A0、A1这种方式简单替代下。

    [
        ["A0", "A1", "A2"],
        ["B3", "B4"],
    ],
    

    举例,所有的结果是 'A0B3','A0B4', 'A1B3', 'A1B4', 'A2B3','A2B4'

    算法核心逻辑是实现一个for循环,如果不是最后一个场次,则当前结果传给下一个递归,再进行for循环,如果是最后一个场次,则组合结果放进公共数组里。

    /**
     * A  'A0','A1','A2'
     * B  'B3','B4'
     * C  'C0','C5'
     */
    let oddResultList = [];
    let teamCombinationList = [
      [["A0", "A1", "A2"]],
      [
        ["A0", "A1", "A2"],
        ["B3", "B4"],
      ],
      [["B3", "B4"]],
      [
        ["A0", "A1", "A2"],
        ["C0", "C5"],
      ],
      [
        ["A0", "A1", "A2"],
        ["B3", "B4"],
        ["C0", "C5"],
      ],
      [
        ["B3", "B4"],
        ["C0", "C5"],
      ],
      [["C0", "C5"]],
    ];
    
    for (let i = 0; i < teamCombinationList.length; i++) {
      const list = teamCombinationList[i];
      againForEach([], 0);
      function againForEach(againList, index) {
        list[index].forEach((el) => {
          if (index === list.length - 1) {
            let str = "";
            [...againList, el].forEach((item) => {
              str += item;
            });
            oddResultList.push(str);
          } else {
            againForEach([...againList, el], index + 1);
          }
        });
      }
    }
    

    oddResultList 的结果如下

    [
      'A0',     'A1',     'A2',     'A0B3',
      'A0B4',   'A1B3',   'A1B4',   'A2B3',
      'A2B4',   'B3',     'B4',     'A0C0',
      'A0C5',   'A1C0',   'A1C5',   'A2C0',
      'A2C5',   'A0B3C0', 'A0B3C5', 'A0B4C0',
      'A0B4C5', 'A1B3C0', 'A1B3C5', 'A1B4C0',
      'A1B4C5', 'A2B3C0', 'A2B3C5', 'A2B4C0',
      'A2B4C5', 'B3C0',   'B3C5',   'B4C0',
      'B4C5',   'C0',     'C5'
    ]
    

    获得所有结果后进行排序,我一开始是调接口实现,后台基于nodejs实现的,当场次少于等于3场返回还是很快,但场次是4的时候,报错内存溢出,我以为是自己算法写的有问题,造成死循环,但是前面也没啥问题...

    因为实际的数据结构比较复杂,我就使用--max-old-space-size命令加大,打开任务管理器,发现内存一致逐渐攀升道6、7千M的位置就不上去,加了几个输出,发现很快就算出结果,因为数组本身存储的是引用,其实这一块是不会内存溢出的,而是卡在了排序上,把排序代码去掉,还是不行,因为要把所有数据序列化后再返给前端,那么这个json是很大的。

    其实所有数据都在前端的时候,是没必要再掉接口的,于是我把代码移到前端执行,4个场次虽然慢了些,但还是能展示,5个场次的时候也不行了,页面崩溃。

    1个场次的时候最多51种,2个场次最多3015,3个场次最多166359,4个场次最多9150603,5个场次仅仅是计算处所有结果页面就崩溃了。至于优化的方式,尝试用简单的标记代替对象,等用到的时候再解析读取,然而一顿操作后还是不行,5个场次有503284374种结果,上亿级别了,我只是执行Array(503284374).fill(1)就崩溃了。

    代码地址: https://github.com/xiaodun/sf-notes

    相关文章

      网友评论

        本文标题:记一次nodejs内存溢出、前端页面崩溃

        本文链接:https://www.haomeiwen.com/subject/rdtkcdtx.html