{"action":"created","issue":{"url":"https://api.github.com/repos/chrisguttandin/standardized-audio-context/issues/410","repository_url":"https://api.github.com/repos/chrisguttandin/standardized-audio-context","labels_url":"https://api.github.com/repos/chrisguttandin/standardized-audio-context/issues/410/labels{/name}","comments_url":"https://api.github.com/repos/chrisguttandin/standardized-audio-context/issues/410/comments","events_url":"https://api.github.com/repos/chrisguttandin/standardized-audio-context/issues/410/events","html_url":"https://github.com/chrisguttandin/standardized-audio-context/issues/410","id":380980920,"node_id":"MDU6SXNzdWUzODA5ODA5MjA=","number":410,"title":"Memory leak with AudioContext ","user":{"login":"TwitchBronBron","id":2544493,"node_id":"MDQ6VXNlcjI1NDQ0OTM=","avatar_url":"https://avatars3.githubusercontent.com/u/2544493?v=4","gravatar_id":"","url":"https://api.github.com/users/TwitchBronBron","html_url":"https://github.com/TwitchBronBron","followers_url":"https://api.github.com/users/TwitchBronBron/followers","following_url":"https://api.github.com/users/TwitchBronBron/following{/other_user}","gists_url":"https://api.github.com/users/TwitchBronBron/gists{/gist_id}","starred_url":"https://api.github.com/users/TwitchBronBron/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TwitchBronBron/subscriptions","organizations_url":"https://api.github.com/users/TwitchBronBron/orgs","repos_url":"https://api.github.com/users/TwitchBronBron/repos","events_url":"https://api.github.com/users/TwitchBronBron/events{/privacy}","received_events_url":"https://api.github.com/users/TwitchBronBron/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2018-11-15T03:10:16Z","updated_at":"2018-11-15T12:48:11Z","closed_at":null,"author_association":"NONE","body":"I think I found a memory leak when using AudioContext. Here's a some sample code and steps to reproduce: \r\n\r\n1. Click the play button once to warm up the page and wait for it to finish.  \r\n2. Then, in chrome dev tools, take a heap snapshot. \r\n3. Then click the \"play\" button again and wait for it to finish. \r\n4. Then take another heap snapshot. Note the memory usage increases.\r\n5. Change the html code from `var ctx = new sac.AudioContext()` to `var ctx = new AudioContext()` (i.e. use the native AudioContext) and repeat the steps above, Note the memory usage stays the same.\r\n\r\nAfter every play iteration, the memory usage goes up. If you run this same code using the native AudioContext object, the memory usage is stable. \r\n\r\n\r\n    <html>\r\n    <body>\r\n        <button id=\"play\" onclick=\"play()\" disabled>Play</button>\r\n        <script type='module'>\r\n            import sac from 'https://dev.jspm.io/npm:standardized-audio-context';\r\n            var ctx = new sac.AudioContext()\r\n            var arrayBuffer;\r\n            async function getArrayBuffer() {\r\n                if (!arrayBuffer) {\r\n                    var response = await fetch('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/Yodel_Sound_Effect.mp3');\r\n                    arrayBuffer = await response.arrayBuffer();\r\n                }\r\n                return arrayBuffer.slice(0);\r\n            }\r\n            window.play = async function () {\r\n                for (var i = 0; i < 100; i++) {\r\n                    console.log(`Playing ${i}`);\r\n                    let source = ctx.createBufferSource();\r\n                    source.buffer = await ctx.decodeAudioData(await getArrayBuffer());\r\n                    source.connect(ctx.destination)\r\n                    source.start();\r\n                    await new Promise((resolve) => {\r\n                        setTimeout(resolve, 50)\r\n                    });\r\n                    source.stop();\r\n                }\r\n            }\r\n            window.onload = () => {\r\n                document.getElementById('play').removeAttribute('disabled');\r\n            };\r\n        </script>\r\n    </body>\r\n    </html>\r\n\r\nstandardized-audio-context memory usage (note how the memory grows over time):\r\n![image](https://user-images.githubusercontent.com/2544493/48527527-7ce64800-e859-11e8-802b-ed94336a6ec9.png)\r\n\r\n\r\nNative AudioContext memory usage (note how the memory does **not** increase over time):\r\n![image](https://user-images.githubusercontent.com/2544493/48527451-37297f80-e859-11e8-8a1d-1147c35316e3.png)\r\n"},"comment":{"url":"https://api.github.com/repos/chrisguttandin/standardized-audio-context/issues/comments/439028905","html_url":"https://github.com/chrisguttandin/standardized-audio-context/issues/410#issuecomment-439028905","issue_url":"https://api.github.com/repos/chrisguttandin/standardized-audio-context/issues/410","id":439028905,"node_id":"MDEyOklzc3VlQ29tbWVudDQzOTAyODkwNQ==","user":{"login":"TwitchBronBron","id":2544493,"node_id":"MDQ6VXNlcjI1NDQ0OTM=","avatar_url":"https://avatars3.githubusercontent.com/u/2544493?v=4","gravatar_id":"","url":"https://api.github.com/users/TwitchBronBron","html_url":"https://github.com/TwitchBronBron","followers_url":"https://api.github.com/users/TwitchBronBron/followers","following_url":"https://api.github.com/users/TwitchBronBron/following{/other_user}","gists_url":"https://api.github.com/users/TwitchBronBron/gists{/gist_id}","starred_url":"https://api.github.com/users/TwitchBronBron/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TwitchBronBron/subscriptions","organizations_url":"https://api.github.com/users/TwitchBronBron/orgs","repos_url":"https://api.github.com/users/TwitchBronBron/repos","events_url":"https://api.github.com/users/TwitchBronBron/events{/privacy}","received_events_url":"https://api.github.com/users/TwitchBronBron/received_events","type":"User","site_admin":false},"created_at":"2018-11-15T12:48:11Z","updated_at":"2018-11-15T12:48:11Z","author_association":"NONE","body":"I'm not sure exactly how you run your tests, but if they run in some form of the chrome browser (like headless chrome), then chrome offers the ` window.performance.memory` object that tells you memory statistics. I adapted my test page to show how to calculate the amount of memory INCREASE over a set of tests. \r\n\r\nHere are the results when I run this in chrome: \r\n\r\n          native audio context:   4,781\r\n    standardized-audio-context: 494,848\r\n\r\nGarbage collection is unpredictable, so you can't just target 0 memory difference.  Sometimes when I run this test, the native test shows negative memory usage over time. That's all due to the fact that we can't control when the garbage collector runs. \r\n\r\nHowever, notice the drastic difference in memory increase over time for standardized-audio-context.  That could be how you detect this issue in a test. Anything over, say, 100,000 fails the test. You want to prevent false positives, but you don't want to miss actual memory leaks. I would make your memory leak test use some huge files so the fluxuation is drastic. Like maybe a 1-2 meg file played 10-100 times. That way you can be confident the test will be over a certain threshold. \r\n\r\n\r\n    <html>\r\n    <body>\r\n        <button id=\"play\" onclick=\"test()\" disabled>Test</button>\r\n        <script type='module'>\r\n            import sac from 'https://dev.jspm.io/npm:standardized-audio-context';\r\n            var ctx;\r\n            var memoryData = [0, 0, 0, 0, 0, 0, 0];\r\n            var arrayBuffer;\r\n            async function getArrayBuffer() {\r\n                if (!arrayBuffer) {\r\n                    var response = await fetch('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/Yodel_Sound_Effect.mp3');\r\n                    arrayBuffer = await response.arrayBuffer();\r\n                }\r\n                return arrayBuffer.slice(0);\r\n            }\r\n\r\n            window.test = async function () {\r\n                ctx = new AudioContext();\r\n                await window.play();\r\n                ctx = new sac.AudioContext()\r\n                await window.play();\r\n            }\r\n            window.play = async function () {\r\n                console.log('Starting tests');\r\n                for (var k = 0; k < memoryData.length; k++) {\r\n                    for (var i = 0; i < 100; i++) {\r\n                        //console.log(`Playing ${i}`);\r\n                        let source = ctx.createBufferSource();\r\n                        source.buffer = await ctx.decodeAudioData(await getArrayBuffer());\r\n                        source.connect(ctx.destination)\r\n                        source.start();\r\n                        await new Promise((resolve) => {\r\n                            setTimeout(resolve, 10)\r\n                        });\r\n                        source.stop();\r\n                    }\r\n                    await new Promise((resolve) => {\r\n                        setTimeout(resolve, 10)\r\n                    });\r\n                    memoryData[k] = window.performance.memory.usedJSHeapSize;\r\n                    console.log('Finished iteration ' + (k + 1));\r\n                }\r\n\r\n                var sum = 0;\r\n                for (var i = 1; i < memoryData.length; i++) {\r\n                    var thisHeapUsage = memoryData[i];\r\n                    var lastHeapUsage = memoryData[i - 1];\r\n                    var amountIncrease = thisHeapUsage - lastHeapUsage;\r\n                    sum += amountIncrease;\r\n                }\r\n                var average = sum / (memoryData.length - 1);\r\n                console.log('Average heap increase', average);\r\n                console.log(memoryData);\r\n            }\r\n            window.onload = () => {\r\n                document.getElementById('play').removeAttribute('disabled');\r\n            };\r\n        </script>\r\n    </body>\r\n\r\n    </html>\r\n\r\n"}}
{"actor":{"display_login":"TwitchBronBron"}}