Javascript 计时器内存泄漏
Javascript timer memory leak
我一直在开发 canvas 游戏,并且 运行 遇到了一些涉及内存泄漏的问题。我认为问题与渲染和删除实体有关,但我 运行 代码没有渲染任何东西,看起来音频调度对象本身导致了泄漏。该问题导致音频开始噼啪作响并在一段时间后中断。游戏仍在渲染,但音频停止了 - 我还注意到角色的射击变得更慢(射击时间与调度程序功能中的注释一起安排)。
我用来处理音频的代码来自A Tale of Two Clocks' tutorial。当我 运行 在 chrome 上节拍器的代码并做了一个时间线记录堆分配上升时。
为什么这段代码会导致内存泄漏?我觉得不错。计时器 ID 设置为 Null?
图 1: 节拍器本身的堆时间线
图 2:我的应用程序的时间表 运行仅节拍器功能(无游戏实体)
图 3:我的应用 运行 正常
这是代码
function init(){
var container = document.createElement( 'div' );
// CREATE CANVAS ... ...
audioContext = new AudioContext();
requestAnimFrame(draw);
timerWorker = new Worker("js/metronomeworker.js");
timerWorker.onmessage = function(e) {
if (e.data == "tick") {
// console.log("tick!");
scheduler();
}
else {
console.log("message: " + e.data);
}
};
timerWorker.postMessage({"interval":lookahead});
}
function nextNote() {
// Advance current note and time by a 16th note...
var secondsPerBeat = 60.0 / tempo; // Notice this picks up the CURRENT
// tempo value to calculate beat length.
nextNoteTime += 0.25 * secondsPerBeat; // Add beat length to last beat time
current16thNote++; // Advance the beat number, wrap to zero
if (current16thNote == 16) {
current16thNote = 0;
}
}
function scheduleNote( beatNumber, time ) {
// push the note on the queue, even if we're not playing.
notesInQueue.push( { note: beatNumber, time: time } );
if ( (noteResolution==1) && (beatNumber%2))
return; // we're not playing non-8th 16th notes
if ( (noteResolution==2) && (beatNumber%4))
return; // we're not playing non-quarter 8th notes
// create an oscillator // create sample
var osc = audioContext.createOscillator();
osc.connect( audioContext.destination );
if (beatNumber % 16 === 0) // beat 0 == low pitch
osc.frequency.value = 880.0;
else if (beatNumber % 4 === 0 ) // quarter notes = medium pitch
osc.frequency.value = 440.0;
else // other 16th notes = high pitch
osc.frequency.value = 220.0;
osc.start( time ); //sound.play(time)
osc.stop( time + noteLength ); // " "
}
function scheduler() {
// while there are notes that will need to play before the next interval,
// schedule them and advance the pointer.
while (nextNoteTime < audioContext.currentTime + scheduleAheadTime ) {
scheduleNote( current16thNote, nextNoteTime );
nextNote();
}
}
function play() {
isPlaying = !isPlaying;
if (isPlaying) { // start playing
current16thNote = 0;
nextNoteTime = audioContext.currentTime;
timerWorker.postMessage("start");
return "stop";
} else {
timerWorker.postMessage("stop");
return "play";
}
}
Metronome.js:
var timerID=null;
var interval=100;
self.onmessage=function(e){
if (e.data=="start") {
console.log("starting");
timerID=setInterval(function(){postMessage("tick");},interval)
}
else if (e.data.interval) {
console.log("setting interval");
interval=e.data.interval;
console.log("interval="+interval);
if (timerID) {
clearInterval(timerID);
timerID=setInterval(function(){postMessage("tick");},interval)
}
}
else if (e.data=="stop") {
console.log("stopping");
clearInterval(timerID);
timerID=null;
}
};
我如何在 scheduleNote() 中安排声音(和拍摄):
if (beatNumber % 4 === 0) {
playSound(samplebb[0], gainNode1);
}
if (planet1play === true) {
if (barNumber % 4 === 0)
if (current16thNote % 1 === 0) {
playSound(samplebb[26], planet1gain);
}
}
if (shootx) {
// Weapon 1
if (gun === 0) {
if (beatNumber === 2 || beatNumber === 6 || beatNumber === 10 || beatNumber === 14) {
shoot(bulletX, bulletY);
playSound(samplebb[3], gainNode2);
}
}
更新
即使我 运行 游戏没有渲染或更新任何东西,音频仍然有问题 Here
在较慢的机器上更糟。
不知道为什么会这样,某种音频缓冲区问题?谁有想法?
如果连续调用start
命令,这里有运行几个setInterval
的风险。如果是,它们会堆积起来,可以解释内存增加的原因。
我建议进行以下更改。如果没有,您可以简单地检查 start 方法中是否存在 timerID,但集中这些方法将有助于保持跟踪。 clearInterval()
可以用 null
参数调用,除了会忽略它之外没有任何后果。
所以本质上:
var timerID = null;
var interval = 100;
function tickBack() { // share callback for timer
postMessage("tick")
}
function startTimer() { // centralize start method
stopTimer(); // can be called with no consequence even if id=null
timerID = setInterval(tickBack, interval)
}
function stopTimer() { // centralize stop method
clearInterval(timerID);
timerID = null;
}
onmessage = function(e){
if (e.data === "start") {
startTimer();
}
else if (e.data === "stop") {
stopTimer()
}
else if (e.data.interval) {
interval = e.data.interval;
if (timerID) startTimer();
}
};
呸!我发现了问题!在我的应用程序中,我创建了一个振荡器但没有使用它,它填满了音频上下文
var osc = audioContext.createOscillator();
osc.connect( audioContext.destination );
我一直在开发 canvas 游戏,并且 运行 遇到了一些涉及内存泄漏的问题。我认为问题与渲染和删除实体有关,但我 运行 代码没有渲染任何东西,看起来音频调度对象本身导致了泄漏。该问题导致音频开始噼啪作响并在一段时间后中断。游戏仍在渲染,但音频停止了 - 我还注意到角色的射击变得更慢(射击时间与调度程序功能中的注释一起安排)。
我用来处理音频的代码来自A Tale of Two Clocks' tutorial。当我 运行 在 chrome 上节拍器的代码并做了一个时间线记录堆分配上升时。
为什么这段代码会导致内存泄漏?我觉得不错。计时器 ID 设置为 Null?
图 1: 节拍器本身的堆时间线
图 2:我的应用程序的时间表 运行仅节拍器功能(无游戏实体)
图 3:我的应用 运行 正常
这是代码
function init(){
var container = document.createElement( 'div' );
// CREATE CANVAS ... ...
audioContext = new AudioContext();
requestAnimFrame(draw);
timerWorker = new Worker("js/metronomeworker.js");
timerWorker.onmessage = function(e) {
if (e.data == "tick") {
// console.log("tick!");
scheduler();
}
else {
console.log("message: " + e.data);
}
};
timerWorker.postMessage({"interval":lookahead});
}
function nextNote() {
// Advance current note and time by a 16th note...
var secondsPerBeat = 60.0 / tempo; // Notice this picks up the CURRENT
// tempo value to calculate beat length.
nextNoteTime += 0.25 * secondsPerBeat; // Add beat length to last beat time
current16thNote++; // Advance the beat number, wrap to zero
if (current16thNote == 16) {
current16thNote = 0;
}
}
function scheduleNote( beatNumber, time ) {
// push the note on the queue, even if we're not playing.
notesInQueue.push( { note: beatNumber, time: time } );
if ( (noteResolution==1) && (beatNumber%2))
return; // we're not playing non-8th 16th notes
if ( (noteResolution==2) && (beatNumber%4))
return; // we're not playing non-quarter 8th notes
// create an oscillator // create sample
var osc = audioContext.createOscillator();
osc.connect( audioContext.destination );
if (beatNumber % 16 === 0) // beat 0 == low pitch
osc.frequency.value = 880.0;
else if (beatNumber % 4 === 0 ) // quarter notes = medium pitch
osc.frequency.value = 440.0;
else // other 16th notes = high pitch
osc.frequency.value = 220.0;
osc.start( time ); //sound.play(time)
osc.stop( time + noteLength ); // " "
}
function scheduler() {
// while there are notes that will need to play before the next interval,
// schedule them and advance the pointer.
while (nextNoteTime < audioContext.currentTime + scheduleAheadTime ) {
scheduleNote( current16thNote, nextNoteTime );
nextNote();
}
}
function play() {
isPlaying = !isPlaying;
if (isPlaying) { // start playing
current16thNote = 0;
nextNoteTime = audioContext.currentTime;
timerWorker.postMessage("start");
return "stop";
} else {
timerWorker.postMessage("stop");
return "play";
}
}
Metronome.js:
var timerID=null;
var interval=100;
self.onmessage=function(e){
if (e.data=="start") {
console.log("starting");
timerID=setInterval(function(){postMessage("tick");},interval)
}
else if (e.data.interval) {
console.log("setting interval");
interval=e.data.interval;
console.log("interval="+interval);
if (timerID) {
clearInterval(timerID);
timerID=setInterval(function(){postMessage("tick");},interval)
}
}
else if (e.data=="stop") {
console.log("stopping");
clearInterval(timerID);
timerID=null;
}
};
我如何在 scheduleNote() 中安排声音(和拍摄):
if (beatNumber % 4 === 0) {
playSound(samplebb[0], gainNode1);
}
if (planet1play === true) {
if (barNumber % 4 === 0)
if (current16thNote % 1 === 0) {
playSound(samplebb[26], planet1gain);
}
}
if (shootx) {
// Weapon 1
if (gun === 0) {
if (beatNumber === 2 || beatNumber === 6 || beatNumber === 10 || beatNumber === 14) {
shoot(bulletX, bulletY);
playSound(samplebb[3], gainNode2);
}
}
更新
即使我 运行 游戏没有渲染或更新任何东西,音频仍然有问题 Here 在较慢的机器上更糟。
不知道为什么会这样,某种音频缓冲区问题?谁有想法?
如果连续调用start
命令,这里有运行几个setInterval
的风险。如果是,它们会堆积起来,可以解释内存增加的原因。
我建议进行以下更改。如果没有,您可以简单地检查 start 方法中是否存在 timerID,但集中这些方法将有助于保持跟踪。 clearInterval()
可以用 null
参数调用,除了会忽略它之外没有任何后果。
所以本质上:
var timerID = null;
var interval = 100;
function tickBack() { // share callback for timer
postMessage("tick")
}
function startTimer() { // centralize start method
stopTimer(); // can be called with no consequence even if id=null
timerID = setInterval(tickBack, interval)
}
function stopTimer() { // centralize stop method
clearInterval(timerID);
timerID = null;
}
onmessage = function(e){
if (e.data === "start") {
startTimer();
}
else if (e.data === "stop") {
stopTimer()
}
else if (e.data.interval) {
interval = e.data.interval;
if (timerID) startTimer();
}
};
呸!我发现了问题!在我的应用程序中,我创建了一个振荡器但没有使用它,它填满了音频上下文
var osc = audioContext.createOscillator();
osc.connect( audioContext.destination );