zoukankan      html  css  js  c++  java
  • H5页游戏内存溢出问题

    • 记录自己解决的第一个H5页的性能问题, 关于内存溢出
    • 拼字游戏

    问题表现

    • 初始化后, 第一次拼字并不卡.

    • 随着拼的次数越来越多, 越来越卡

    • 浏览器任务管理器中可以看出, 内存持续升高

    • 确定内存问题, 即是卡顿第一问题

      内存飙升

    代码层面问题

    • 首先希望从代码层面排查内存问题
    • 思考代码后, 发现以下两点

    第二舞台直接删除Canvas的Dom

    • 问题:
      • 每次都会重新建立第二个分享舞台.
      • 上一次的canvas直接删除了Dom.
    • 推断:
      • 推断直接删除DOM, 并不能释放舞台上一系列的Bitmap图像等
      • 上几次图像继续占用内存
    • 排查与解决:
      • 游戏舞台与分享舞台只保留一个
      • 每次游戏, 通过指针清空bitmap
    • 结果:
      • 内存无明显变化

    loader进来的临时图片未删除

    • 问题:
      • 每次完成一次拼字都会请求后台这次结果需要加载的资源
      • 资源中包含大量的图片等, 都是通过new Image()的src加载过来的
    • 推断:
      • 每次浏览器加载大量图片, 展示后, 并未释放图片空间
    • 排查与解决:
      • 找出不能重复利用的图片, 使用新的加载器
      • 一次性使用图片后, 让加载器为null
    • 结果:
      • 内存少量减少
      • 游戏已经卡顿

    每次游戏会重新new一些对象

    • 问题: 因每次进行游戏都会new一些对象出来
    • 推断: 怀疑是游戏过程中js内存溢
    • 排查: js内存变化
    • 结论: js中的内存变化不能引起内存卡顿问题.

    GPU层面问题

    • 直接贴链接了: Chromium Graphics // Chrome GPU
    • 这次通过任务管理器可以直接看出是GPU缓存的溢出
    • 哭了, 为什么上次我测得时候不显示... 仅仅是一个内存的表示, 给我好找啊..
    • 最后还是在安卓中进行排查, 才知道是GPU / chromium grapics的问题

    观察GPU表现体征

    • 我们在游戏中发现内存的溢出主要是GPU的变化
    • 在游戏中去除所有的序列帧后, 只保留最基本的游戏, 任何手机都不卡.
    • 那就一定是这些图片搞得鬼.
    • 已经把加载的图片缓存清除掉了.
    • 我们排除了canvas, 排除了图片加载, 剩下的就是最基本的图片展示了.
    • 现在唯一剩下的就是浏览器自己做的事情了.
    • 展示图片, 然后清理DOM
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <style>
      
      #stage {
         100%;
        height: 100%;
      }
      </style>
      <title>测试内存</title>
    </head>
    
    <body>
        <button id="add">增加图片</button>
        <button id="clear">清除内存</button>
        <button id="addTen">增加十倍图片</button>
        <div id="stage"></div>
        <script>
        var count = 0
        var stage = document.getElementById('stage')
        document.getElementById('add').onclick = function () {
          for (var i = 0; i < 23; i++) {
            stage.innerHTML += `<img src='./img${count}/${i}.jpg' />`
          }
          ++count
        }
        document.getElementById('clear').onclick = function () {
          stage.innerHTML = ''
          count = 0
        }
        document.getElementById('addTen').onclick = function () {
          for (var j = 0; j < 10; j++) {
            for (var i = 0; i < 23; i++) {
              stage.innerHTML += `<img src='./img${count}/${i}.jpg' />`
            }
          }
        }
        </script>
    </body>
    
    </html>
    

    chrome中测试

    • 在添加三组十倍图片后, 清除DOM
    • GPU和Image cache, 在浏览器中并不能总结出规律

    webview表现特征

    • 我们写了一个最简单的demo, 在安卓上的webview进行了测试
    • 测试结果如下:
      • 初始状态下的内存: init
      • 添加图片并浏览器后的内存: add
      • 清理页面dom后的内存: clear

    总结

    个人理解原理

    浏览器中的GPU会自动缓存一段时间展示过的 "图像". 我们称为: "GPU Program Cache"

    • 浏览器在读取图片之后会生成GPU可以使用的着色器代码
    • 在GPU使用的时候, 会自动缓存这些着色器代码.

    最后解决办法

    • 去掉大量序列帧, 序列帧尽量用小图
    • 动画尽量用代码实现

    附带一个测试着色器的代码, 也是当时测试webgl的代码

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
    </head>
    
    <body>
      <canvas id="glcanvas" width="700" height="500"></canvas>
      <script>
        // 获取WebGL
        var canvas = document.getElementById('glcanvas')
        gl = canvas.getContext("webgl")
    
        // 创建顶点着色器
        var VSHADER_SOURCE =
          'void main() {
    ' +
          '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
    ' +
          '  gl_PointSize = 10.0;
    ' +
          '}
    ';
        // 创建片元着色器
        // var FSHADER_SOURCE =
        //   'void main() {
    ' +
        //   '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    ' +
        //   '}
    ';
    
        // 绘制圆
        var FSHADER_SOURCE = `
          #ifdef GL_ES
            precision mediump float;
          #endif
            void main() {
              float d = distance(gl_PointCoord, vec2(0.5,0.5));
              if(d < 0.5){
                gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
              }else{ discard;} 
            }`;
    
        // 着色器代码需要放到一个程序中
        var program = gl.createProgram()
        // 创建顶点着色器
        var vShader = gl.createShader(gl.VERTEX_SHADER)
        // 创建片元着色器
        var fShader = gl.createShader(gl.FRAGMENT_SHADER)
    
        // shader容器与着色器绑定
        gl.shaderSource(vShader, VSHADER_SOURCE)
        gl.shaderSource(fShader, FSHADER_SOURCE)
    
        // 将GLSE语言编写成浏览器可用的代码
        gl.compileShader(vShader)
        gl.compileShader(fShader)
    
        // 将着色器添加到程序上
        gl.attachShader(program, vShader)
        gl.attachShader(program, fShader)
    
        // 链接程序
        // 在链接操作以后, 可以任意修改shader代码.
        // 对shader重新编译不会影响整个程序, 除非重新链接程序
        gl.linkProgram(program)
    
        // 加载并使用链接好的程序
        gl.useProgram(program)
    
        // 绘制一个点
        gl.clearColor(0.0, 0.0, 0.0, 1.0)
        gl.clear(gl.COLOR_BUFFER_BIT)
        gl.drawArrays(gl.POINTS, 0, 1)
      </script>
    </body>
    
    </html>
    
  • 相关阅读:
    sql的优化
    使用Robo 3T访问MongoDB数据库
    在IDEA中用三个jar包链接MongoDB数据库——实现增删改查
    使用Robo 3T操作MongoDB数据库
    MongoDB 创建数据库
    mysql本地中127.0.0.1连接不上数据库怎么办
    IDEA-Maven的Dependencies中出现红色波浪线
    log4j2+slf4j+junit
    fastxml Jackson annotation使用小记
    我理解的互联网技术领域
  • 原文地址:https://www.cnblogs.com/zhangrunhao/p/11127702.html
Copyright © 2011-2022 走看看