可能的内存泄漏或别的东西? [英] Possible memory leak or something else?

查看:168
本文介绍了可能的内存泄漏或别的东西?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在javascript中创建了一个可视化器,当您选择一个音乐目录时,您可以选择该目录中的文件进行播放并让可视化器移至。但是,在加载一个目录并且然后改变歌曲超过4次之后似乎会导致可视化器的响应速度变慢。我不确定为什么会发生这种情况。
这里有一个例子。



不断从下拉框中改变歌曲,直到您看到它开始变慢。



  window.onload = function(){var input = document.getElementById(file); var audio = document.getElementById(audio); var selectLabel = document.querySelector(label [for = select]); var audioLabel = document.querySelector(label [for = audio]); var select = document.querySelector(select); var context = void 0,src = void 0,res = [],url =; function processDirectoryUpload(event){var webkitResult = []; var mozResult = []; var文件;的console.log(事件); select.innerHTML =; //做mozilla的东东函数mozReadDirectories(entries,path){console.log(dir,entries,path); return().reduce.call(entries,function(promise,entry){return promise.then(function(){return Promise.resolve(entry.getFilesAndDirectories()|| entry).then(function(dir){return dir })}),Promise.resolve()).then(function(items){var dir = items.filter(function(folder){return folder instanceof Directory}); var files = items.filter(function(file) {返回文件instanceof文件}); if(files.length){// console.log(files:,files,path); mozResult = mozResult.concat.apply(mozResult,files);} if(dir.length ){// console.log(dir,dir [0] instanceof Directory); return mozReadDirectories(dir,dir [0] .path || path);} else {if(!dir.length){return Promise.resolve mozResult).then(功能(完成){返回完成})}}})};函数handleEntries(entry){let file =webkitGetAsEntryin entry? entry.webkitGetAsEntry():入口返回Promise.resolve(file); }函数handleFile(entry){return new Promise(function(resolve){if(entry.isFile){entry.file(function(file){listFile(file,entry.fullPath).then(resolve)})} if if (entry.isDirectory){var reader = entry.createReader(); reader.readEntries(webkitReadDirectories.bind(null,entry,handleFile,resolve))} else {var entries = [entry]; return entries.reduce(function(promise ,file){return promise.then(function(){return listDirectory(file)})},Promise.resolve()).then(function(){return Promise.all(entries.map(function(file){return listFile(file)}))。then(resolve)})}})函数webkitReadDirectories(entry,callback,resolve,entries){console.log(entries);返回listDirectory(entry).then(function(currentDirectory){console.log(`迭代$ {currentDirectory.name} directory`,entry);返回entries.reduce(function(promise,directory){return promise.then(function( ){return callback(directory)});},Promise.resolve())})。then(resolve); }}函数listDirectory(entry){console.log(entry);返回Promise.resolve(入口); }函数listFile(文件,路径){路径=路径|| file.webkitRelativePath || /+ file.name; console.log(`读取$ {file.name},大小:$ {file.size},路径:$ {path}`); webkitResult.push(文件);返回Promise.resolve(webkitResult)};函数processFiles(文件){Promise.all([]。map.call(files,function(file,index){return handleEntries(file,index).then(handleFile)})).then(function(){console。 log(complete,webkitResult); res = webkitResult; res.reduce(function(promise,track){return promise.then(function(){return playMusic(track)})} displayFiles(res))})。 catch(function(err){alert(err.message);})} if(getFilesAndDirectoriesin event.target){return(event.type ===drop?event.dataTransfer:event.target).getFilesAndDirectories ().dhen(function(dir){if(dir [0] instanceof Directory){console.log(dir)return mozReadDirectories(dir,dir [0] .path || path).then(function(complete){console .log(complete:,webkitResult); event.target.value = null;});} e lse {if(dir [0] instanceof File&& dir [0] .size> 0){return Promise.resolve(dir).then(function(){console.log(complete:,mozResult); res = mozResult; res.reduce(function(promise,track){return promise.then(function (){}}}}} displayFiles(res))})else {if(dir [0] .size == 0){throw new Error(could not process'+ dir [0] .name +'directory+在firefox的drop event,在'Choose folder ...'输入上传文件夹'); catch(function(err){alert(err)})} files = event.target.files;}}}}}} if(files){processFiles(files)}} function displayFiles(files){select.innerHTML =;返回Promise.all(files.map(函数(文件,索引){返回新的Promise(函数(解析){如果(/^audio/.test(file.type)){/ *做的东西,这是所有代码当前在Promise解析器函数中* /} else {/ *继续下一个文件* / resolve()} var option = new Option(file.name,index); select.appendChild(option); resolve()})}))}函数handleSelectedSong(event){if(res.length){var index = select.value; var track = res [index]; playMusic(track).then(function(filename){console.log(filename +playback completed)})} else {console.log(No songs to play)}} playMusic(file){return new Promise (function(resolve){audio.pause(); audio.onended = function(){audio.onended = null; if(url)URL.revokeObjectURL(url); resolve(file.name);} if(url)URL .revokeObjectURL(url); url = URL.createObjectURL(file); audio.load(); audio.src = url; audio.play(); audioLabel.textContent = file.name; context = context || new AudioContext() ; src = src || context.createMediaElementSource(audio); src.disconnect(context); var analyzer = context.createAnalyser(); var canvas = document.getElementById(canvas); canvas.width = window.innerWidth; canvas .height = window.innerHeight; var ctx = canvas.getContext(2d); src.connect(analyzer); analyser.connect(context.destina荐); analyser.fftSize = 16384; var bufferLength = analyser.frequencyBinCount;的console.log量(bufferLength); var dataArray = new Uint8Array(bufferLength); var WIDTH = canvas.width; var HEIGHT = canvas.height; var barWidth =(WIDTH / bufferLength)* 32; var barHeight; var x = 0;函数renderFrame(){requestAnimationFrame(renderFrame); x = 0; analyser.getByteFrequencyData(dataArray中); ctx.fillStyle =#1b1b1b; ctx.fillRect(0,0,WIDTH,HEIGHT); for(var i = 0; i< bufferLength; i ++){barHeight = dataArray [i]; ctx.fillStyle =rgb(5,155,45)ctx.fillRect(x,(((HEIGHT  -  barHeight  -  5%barHeight)+(20%HEIGHT  -  barHeight))),barWidth,barHeight + 20%HEIGHT); x + = barWidth + 2; }} renderFrame(); })} input.addEventListener(change,processDirectoryUpload); select.addEventListener(change,handleSelectedSong);}  

< canvas id =canvaswidth =window.innerWidthheight =window.innerHeight>< / canvas>< div id =content> < label class =custom-file-upload>选择音乐目录< input id =filetype =fileaccept =audio / *directory allowdirs webkitdirectory /> < p style =color:rgb(5,195,5);>正在播放:< label for =audio>< / label>< / p> < p style =color:rgb(5,195,5);>选择歌曲< / p> < select id =select> < /选择> < audio id =audiocontrols>< / audio>


因此,当您播放5首歌曲时,您仍然有5个可视化渲染循环在每一帧运行,还有5个分析器。

这里最好做的是重构您的代码:


  • 创建一个分析器,只更新提供该分析器的流
  • 由于您只有一个< canvas> ,因此只能启动一个渲染动画



使用单个分析器时,渲染器在更改源代码时不会使用任何新东西,它始终是同一个画布,相同的分析仪,相同的可视化。



这是一个概念的快速证明,非常肮脏,但我希望你能够承担我所做的事情和原因。 / p>

wind ow.onload = function(){var input = document.getElementById(file); var audio = document.getElementById(audio); var selectLabel = document.querySelector(label [for = select]); var audioLabel = document.querySelector(label [for = audio]); var select = document.querySelector(select); var viz = null; //删除所有IDK对于目录特殊处理函数的意义function displayFiles(){select.innerHTML =; //这是完全同步的,为什么Promises? res = Array.prototype.slice.call(input.files); res.forEach(function(file,index){if(/^audio/.test(file.type)){var option = new Option(file.name,index); select.appendChild(option);}});如果(res.length){var analyzer = initAudioAnalyser(); viz = initVisualization(分析器); //预先选择第一首歌曲? handleSelectedSong(); audio.pause(); }} function handleSelectedSong(event){if(res.length){var index = select.value; var track = res [index]; playMusic(track).then(function(filename){console.log(filename +playback completed)})viz.play(); } else {console.log(No songs to play)}} function playMusic(file){return new Promise(function(resolve){var url = audio.src; audio.pause(); audio.onended = function ){audio.onended = null; //这里没有用处,因为blobURIs只是指向用户系统上的真实文件的指针,如果(url)URL.revokeObjectURL(url); resolve(file.name);} if(url)URL。 revokeObjectURL(url); url = URL.createObjectURL(file); // audio.load(); //因为您在audio.src = url; audio.play(); audioLabel之前撤消了URL,所以只会设置一个404。 textContent = file.name;}); }函数initAudioAnalyser(){var context = new AudioContext(); var analyzer = context.createAnalyser(); analyser.fftSize = 16384; var src = context.createMediaElementSource(audio); src.connect(分析仪); src.connect(context.destination);返回分析仪; }函数initVisualization(分析器){var canvas = document.getElementById(canvas); canvas.width = window.innerWidth; canvas.height = window.innerHeight; var ctx = canvas.getContext(2d); var bufferLength = analyser.frequencyBinCount; var dataArray = new Uint8Array(bufferLength); var WIDTH = canvas.width; var HEIGHT = canvas.height; var barWidth =(WIDTH / bufferLength)* 32; var barHeight; var x = 0; var paused = true;函数renderFrame(){if(!paused){requestAnimationFrame(renderFrame); } else {return; } x = 0; analyser.getByteFrequencyData(dataArray中); ctx.fillStyle =#1b1b1b; ctx.fillRect(0,0,WIDTH,HEIGHT); ctx.fillStyle =rgb(5,155,45)ctx.beginPath(); for(var i = 0; i< bufferLength; i ++){barHeight = dataArray [i]; //微优化,但是在单个形状中连接所有Rect对于ctx.rect(x,(((HEIGHT - barHeight - 5%barHeight)+(20%HEIGHT - barHeight))),barWidth, barHeight + 20%高度); x + = barWidth + 2; } ctx.fill(); } var viz = window.viz = {play:function(){if(paused){paused = false; renderFrame(); }},暂停:function(){paused = true; clearTimeout(pauseTimeout); pauseTimeout = null; },}; //我们甚至可以添加链接到音频元素的自动暂停var pauseTimeout = null; audio.onpause = function(){//让我们真的在2秒内做到这一点,以保持撕下效果pauseTimeout = setTimeout(viz.pause,2000); } audio.onplaying = function(){clearTimeout(pauseTimeout); //我们不玩if(!pauseTimeout){viz.play(); }} return viz; } input.addEventListener(change,displayFiles); select.addEventListener(change,handleSelectedSong);}

< canvas id =canvaswidth =window.innerWidthheight =window.innerHeight>< / canvas>< div id =content> < label class =custom-file-upload>选择音乐目录< input id =filetype =fileaccept =audio / *directory allowdirs webkitdirectory /> < p style =color:rgb(5,195,5);>正在播放:< label for =audio>< / label>< / p> < p style =color:rgb(5,195,5);>选择歌曲< / p> < select id =select> < /选择> < audio id =audiocontrols>< / audio>

I've made a visualizer in javascript that when you select a music directory your're able to select files within that directory to play and have the visualizer move to. But it would seem after loading a directory and then changing the song more than 4 times Results in less responsive movement from the visualizer. I'm unsure why this is happening. Heres an example of this happening.

Keep changing the song from the drop down box until you see it starting to slow.

window.onload = function() {
  var input = document.getElementById("file");
  var audio = document.getElementById("audio");
  var selectLabel = document.querySelector("label[for=select]");
  var audioLabel = document.querySelector("label[for=audio]");
  var select = document.querySelector("select");
  var context = void 0,
    src = void 0,
    res = [],
    url = "";

  function processDirectoryUpload(event) {
    var webkitResult = [];
    var mozResult = [];
    var files;
    console.log(event);
    select.innerHTML = "";

    // do mozilla stuff
    function mozReadDirectories(entries, path) {
      console.log("dir", entries, path);
      return [].reduce.call(entries, function(promise, entry) {
          return promise.then(function() {
            return Promise.resolve(entry.getFilesAndDirectories() || entry)
              .then(function(dir) {
                return dir
              })
          })
        }, Promise.resolve())
        .then(function(items) {
          var dir = items.filter(function(folder) {
            return folder instanceof Directory
          });
          var files = items.filter(function(file) {
            return file instanceof File
          });
          if (files.length) {
            // console.log("files:", files, path);
            mozResult = mozResult.concat.apply(mozResult, files);
          }
          if (dir.length) {
            // console.log(dir, dir[0] instanceof Directory);
            return mozReadDirectories(dir, dir[0].path || path);

          } else {
            if (!dir.length) {
              return Promise.resolve(mozResult).then(function(complete) {
                return complete
              })
            }
          }

        })

    };

    function handleEntries(entry) {
      let file = "webkitGetAsEntry" in entry ? entry.webkitGetAsEntry() : entry
      return Promise.resolve(file);
    }

    function handleFile(entry) {
      return new Promise(function(resolve) {
        if (entry.isFile) {
          entry.file(function(file) {
            listFile(file, entry.fullPath).then(resolve)
          })
        } else if (entry.isDirectory) {
          var reader = entry.createReader();
          reader.readEntries(webkitReadDirectories.bind(null, entry, handleFile, resolve))
        } else {
          var entries = [entry];
          return entries.reduce(function(promise, file) {
              return promise.then(function() {
                return listDirectory(file)
              })
            }, Promise.resolve())
            .then(function() {
              return Promise.all(entries.map(function(file) {
                return listFile(file)
              })).then(resolve)
            })
        }
      })

      function webkitReadDirectories(entry, callback, resolve, entries) {
        console.log(entries);
        return listDirectory(entry).then(function(currentDirectory) {
          console.log(`iterating ${currentDirectory.name} directory`, entry);
          return entries.reduce(function(promise, directory) {
            return promise.then(function() {
              return callback(directory)
            });
          }, Promise.resolve())
        }).then(resolve);
      }

    }

    function listDirectory(entry) {
      console.log(entry);
      return Promise.resolve(entry);
    }

    function listFile(file, path) {
      path = path || file.webkitRelativePath || "/" + file.name;
      console.log(`reading ${file.name}, size: ${file.size}, path:${path}`);
      webkitResult.push(file);
      return Promise.resolve(webkitResult)
    };

    function processFiles(files) {
      Promise.all([].map.call(files, function(file, index) {
          return handleEntries(file, index).then(handleFile)
        }))
        .then(function() {
          console.log("complete", webkitResult);
          res = webkitResult;
          res.reduce(function(promise, track) {
            return promise.then(function() {
              return playMusic(track)
            })
          }, displayFiles(res))
        })
        .catch(function(err) {
          alert(err.message);
        })
    }

    if ("getFilesAndDirectories" in event.target) {
      return (event.type === "drop" ? event.dataTransfer : event.target).getFilesAndDirectories()
        .then(function(dir) {
          if (dir[0] instanceof Directory) {
            console.log(dir)
            return mozReadDirectories(dir, dir[0].path || path)
              .then(function(complete) {
                console.log("complete:", webkitResult);
                event.target.value = null;
              });
          } else {
            if (dir[0] instanceof File && dir[0].size > 0) {
              return Promise.resolve(dir)
                .then(function() {
                  console.log("complete:", mozResult);
                  res = mozResult;
                  res.reduce(function(promise, track) {
                    return promise.then(function() {
                      return playMusic(track)
                    })
                  }, displayFiles(res))
                })
            } else {
              if (dir[0].size == 0) {
                throw new Error("could not process '" + dir[0].name + "' directory" + " at drop event at firefox, upload folders at 'Choose folder...' input");
              }
            }
          }
        }).catch(function(err) {
          alert(err)
        })
    }

    files = event.target.files;

    if (files) {
      processFiles(files)
    }

  }

  function displayFiles(files) {
    select.innerHTML = "";
    return Promise.all(files.map(function(file, index) {
      return new Promise(function(resolve) {
        if (/^audio/.test(file.type)) { /* do stuff, that is all code currently within Promise resolver function */ } else { /* proceed to next file */
          resolve()
        }
        var option = new Option(file.name, index);
        select.appendChild(option);
        resolve()
      })
    }))
  }

  function handleSelectedSong(event) {
    if (res.length) {
      var index = select.value;
      var track = res[index];
      playMusic(track)
        .then(function(filename) {
          console.log(filename + " playback completed")
        })
    } else {
      console.log("No songs to play")
    }
  }

  function playMusic(file) {
    return new Promise(function(resolve) {
      audio.pause();
      audio.onended = function() {
        audio.onended = null;
        if (url) URL.revokeObjectURL(url);
        resolve(file.name);
      }
      if (url) URL.revokeObjectURL(url);
      url = URL.createObjectURL(file);
      audio.load();
      audio.src = url;
      audio.play();
      audioLabel.textContent = file.name;
      context = context || new AudioContext();
      src = src || context.createMediaElementSource(audio);
      src.disconnect(context);

      var analyser = context.createAnalyser();

      var canvas = document.getElementById("canvas");
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
      var ctx = canvas.getContext("2d");

      src.connect(analyser);
      analyser.connect(context.destination);

      analyser.fftSize = 16384;

      var bufferLength = analyser.frequencyBinCount;
      console.log(bufferLength);

      var dataArray = new Uint8Array(bufferLength);
      var WIDTH = canvas.width;
      var HEIGHT = canvas.height;

      var barWidth = (WIDTH / bufferLength) * 32;
      var barHeight;
      var x = 0;

      function renderFrame() {
        requestAnimationFrame(renderFrame);
        x = 0;

        analyser.getByteFrequencyData(dataArray);

        ctx.fillStyle = "#1b1b1b";
        ctx.fillRect(0, 0, WIDTH, HEIGHT);

        for (var i = 0; i < bufferLength; i++) {
          barHeight = dataArray[i];


          ctx.fillStyle = "rgb(5,155,45)"
          ctx.fillRect(x, (((HEIGHT - barHeight - 5 % barHeight) + (20 % HEIGHT - barHeight))), barWidth, barHeight + 20 % HEIGHT);

          x += barWidth + 2;
        }
      }

      renderFrame();
    })

  }

  input.addEventListener("change", processDirectoryUpload);
  select.addEventListener("change", handleSelectedSong);
}

<canvas id="canvas" width="window.innerWidth" height="window.innerHeight"></canvas>
<div id="content">
  <label class="custom-file-upload">
  Select Music directory <input id="file" type="file" accept="audio/*" directory allowdirs webkitdirectory/>
   <p style="color: rgb(5,195,5);">Now playing:<label for="audio"></label></p>

  <p style="color: rgb(5,195,5);">Select Song</p>
  <select id="select">
    </select>
  <audio id="audio" controls></audio>

解决方案

Like noted by yuriy636, you are starting a new animation for every new song, without never stopping the previous one. So when you played 5 songs, you still have 5 visualizations rendering loop running at every frame, and 5 analyzers.

The best to do here is to refactor your code :

  • create a single analyzer, only update which stream feeds it
  • make the canvas animation autonomous, declare it once at first load
  • since you've got only one <canvas>, start only one rendering animation

When using a single analyzer, your render doesn't use anything new when you change the source, it's always the same canvas, the same analyzer, the same visualization.

Here is a quick proof of concept, really dirty, but I hope you'll be able to undertstand what I did and why.

window.onload = function() {
  var input = document.getElementById("file");
  var audio = document.getElementById("audio");
  var selectLabel = document.querySelector("label[for=select]");
  var audioLabel = document.querySelector("label[for=audio]");
  var select = document.querySelector("select");


  var viz = null;

  // removed all the IDK what it was meant for directory special handlers

  function displayFiles() {
    select.innerHTML = "";
    // that's all synchronous, why Promises ?
    res = Array.prototype.slice.call(input.files);
    res.forEach(function(file, index) {
      if (/^audio/.test(file.type)) {
        var option = new Option(file.name, index);
        select.appendChild(option);
      }
    });

    if (res.length) {
      var analyser = initAudioAnalyser();
      viz = initVisualization(analyser);
      // pre-select the first song ?
      handleSelectedSong();
      audio.pause();
    }
  }

  function handleSelectedSong(event) {
    if (res.length) {
      var index = select.value;
      var track = res[index];
      playMusic(track)
        .then(function(filename) {
          console.log(filename + " playback completed")
        })
      viz.play();
    } else {
      console.log("No songs to play")
    }
  }

  function playMusic(file) {
    return new Promise(function(resolve) {
      var url = audio.src;
      audio.pause();
      audio.onended = function() {
        audio.onended = null;
        // arguablily useless here since blobURIs are just pointers to real file on the user's system
        if (url) URL.revokeObjectURL(url);
        resolve(file.name);
      }

      if (url) URL.revokeObjectURL(url);
      url = URL.createObjectURL(file);
      //      audio.load(); // would just set a 404 since you revoked the URL just before
      audio.src = url;
      audio.play();
      audioLabel.textContent = file.name;

    });
  }

  function initAudioAnalyser() {
    var context = new AudioContext();
    var analyser = context.createAnalyser();
    analyser.fftSize = 16384;

    var src = context.createMediaElementSource(audio);
    src.connect(analyser);
    src.connect(context.destination);

    return analyser;

  }

  function initVisualization(analyser) {

    var canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    var ctx = canvas.getContext("2d");

    var bufferLength = analyser.frequencyBinCount;

    var dataArray = new Uint8Array(bufferLength);
    var WIDTH = canvas.width;
    var HEIGHT = canvas.height;

    var barWidth = (WIDTH / bufferLength) * 32;
    var barHeight;
    var x = 0;

    var paused = true;
    
    function renderFrame() {
      if (!paused) {
        requestAnimationFrame(renderFrame);
      } else {
        return;
      }
      x = 0;

      analyser.getByteFrequencyData(dataArray);

      ctx.fillStyle = "#1b1b1b";
      ctx.fillRect(0, 0, WIDTH, HEIGHT);

      ctx.fillStyle = "rgb(5,155,45)"
      ctx.beginPath();
      for (var i = 0; i < bufferLength; i++) {
        barHeight = dataArray[i];
        // micro-optimisation, but concatenating all the rects in a single shape is easier for the CPU
        ctx.rect(x, (((HEIGHT - barHeight - 5 % barHeight) + (20 % HEIGHT - barHeight))), barWidth, barHeight + 20 % HEIGHT);
        x += barWidth + 2;
      }
      ctx.fill();
    }
    var viz = window.viz = {
      play: function() {
        if(paused){
          paused = false;
          renderFrame();
          }
      },
      pause: function() {
        paused = true;
        clearTimeout(pauseTimeout);
        pauseTimeout = null;
      },
    };
    // we can even add auto pause linked to the audio element
    var pauseTimeout = null;
    audio.onpause = function() {
      // let's really do it in 2s to keep the tear down effect
      pauseTimeout = setTimeout(viz.pause, 2000);
    }
    audio.onplaying = function() {
      clearTimeout(pauseTimeout);
      // we were not playing
      if(!pauseTimeout){
        viz.play();
        }
    }
    return viz;
  }

  input.addEventListener("change", displayFiles);
  select.addEventListener("change", handleSelectedSong);
}

<canvas id="canvas" width="window.innerWidth" height="window.innerHeight"></canvas>
<div id="content">
  <label class="custom-file-upload">
  Select Music directory <input id="file" type="file" accept="audio/*" directory allowdirs webkitdirectory/>
   <p style="color: rgb(5,195,5);">Now playing:<label for="audio"></label></p>

  <p style="color: rgb(5,195,5);">Select Song</p>
  <select id="select">
    </select>
  <audio id="audio" controls></audio>

这篇关于可能的内存泄漏或别的东西?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆