悪い例まずは悪い例です。例の問題ありのWEBアプリを単純化しました。
プログラムの概要は、特定の画像フォルダを走査し、画像ファイルだけを取得し、ブラウザで決まった枚数づつ表示させます。
いわゆる画像ビュアーです。画像の枚数は、普段は数万点を扱いますが今回は検証しやすい様に5点にしました。
import { getData } from './modules/getData.js';
const i = {
_data: [],
get data(){return this._data},
set data(v){this._data= v},
imgObjects: [],
programuPath: '../',
thumbdir: 'images',
extension: 'webp',
displayCount: 2
};
async function setready() {
await getData(i);
changeImg();
}
setready();
/* ここまでで画像フォルダから画像ファイルを収集 */
const gallery= document.querySelector('#gallery');
const button= document.querySelector('button');
button.addEventListener('click', changeImg, false);
let num= 0;
/* 画像変更ボタンを押した時に起動するchangeImg関数 */
function changeImg(){
const totalImgCount= i.data.length;
const totalPageCount= Math.ceil( totalImgCount/i.displayCount );
let imgtags= [];
if(num>= totalPageCount){
num= 0;
}
/* i.data配列の中に画像ファイル名が格納されている
* 表示する数だけIMG要素を作成し画像の配列へ格納し
*/
i.data.forEach(function(v, k){
if(i.displayCount*num <= k && k < i.displayCount*(num +1 )){
imgtags.push( `<img src="../images/${v}" alt="images/${v}">`);
}
});
num++;
/*
* 配列のすべての要素を連結して変数に変換。
* それをID=galleryの要素内へ入れ替え。
*/
gallery.innerHTML = imgtags.join('');
}
開発ツールで調べてみると問題点が明確にfirefoxの開発ツールで調べてみるとたった5点しかないはずの画像が、メモリ内でどんどん増殖していくのを確認。
最初は画像を2点表示するだけなので、画像は2個表示されている。
ところが、画像変換を繰り返すと、画像は5点しかないはずなのに、メモリには5個以上保存されている 原因究明5点しかないはずの画像を繰り返し表示させると、どんどん画像がメモリに蓄積されていく。
つまり画像のメモリ解放が正しくおこなわれていないということ。
どうすれば画像のメモリを解放できるのか、それには画像の参照を切ればメモリから解放されるということなので、まずは表示が済んだ画像や要素は即削除することに
また、gallery.firstChild.src = ""; で明示的に画像の参照を切ったり、不要な配列や変数の参照を解除したり・・・。
が、それでも変わらず、メモリは増え続ける。
それならとgallery内に子要素を追加する前に必ずクリアにする、innerHTMLを.replaceChildrenに変更したり、while ループで明示的に削除するなどいろいろ試すが、まったく効果なし。
画像のメモリ管理を確実にする現状は画像は直接、img要素に記述してからgallery要素内へ追加しているが、それをいったん画像のみをメモリ内へプールし、表示させるべき順番がきたら保存しているが画像を共有し表示させる仕様に変更。
つまり5点の画像をメモリに保存してしまい、必要な時に差し替えるようにすれば、画像メモリは5個以上にはならないという考え。
そのサンプルが以下。
画像を共有前提の実装見本:https://labo.studio-happyvalley.com/garbage_collection/savememory/
画像のメモリリークは解消しているようだ今度のコードは画像が5個以上メモリに残らないように動作しているかを確認するのがポイントなので、画像変換ボタンを押すたびに画像メモリの中身をコンソールログへ書き出すように。その結果が以下。
初期状態では画像は2点のみ表示なので画像メモリにも2点のみ格納されている
段目が2ページ目の表示。2段目が3ページ目の表示。 3段目は二度目の1ページ目。つまり1週しても画像メモリの中身は5個のままで増えていない。画像自体にはメモリリークは発生していないことが確認できた。 しかしそれでもIMG要素の増加はとまらない画像のメモリリークは解消しているので、IMG要素の増加はストップしたはずと思い開発ツールで確認。
が、変わらず、増え続けていく・・・。
変わらず、5個を超えてIMG要素は増え続ける・・・。 画像のメモリリークは解消したはずなのに、なぜIMG要素が増え続ける画像のメモリリークは解消しているのにIMG要素が増え続けるのはなぜか、もっとも論理的な結論はIMG要素のみが増え続けている!!ということ。つまりメモリタブの増え続けるIMG要素は画像ではなく、文字通りIMG要素だけなのでは!という推論。
さっそくその推測に従いコードを変更することに。

IMG要素を最初から準備しておくように変更したコード推論にしたがい、1ページに表示する画像枚数分IMG要素をgalleryオブジェクト内へ配置。
IMG要素を最初から準備サンプル:https://labo.studio-happyvalley.com/garbage_collection/good/