FileReader利用時のメモリ増減パターンをちょっと調べてみた

FileReaderを通してローカル画像ファイルのプレビュー

jsFiddleを使ってみたかったので,デモ的に貼ってみます.

このあたりを参考に,FileReaderでローカルの画像ファイルを読み込み&ファイルプレビューをしてみました.大体動くんですが,どうもブラウザがメモリとってもおいしいです☆をしていたので,ちょっと調べてみました.

メモリリークするケース?

で,どうもFileReaderのインスタンスを作ったらしっかり使い回さないと,狭い確認範囲ですがメモリリークすることがあるみたいです.正直,ガベージコレクションに明るくなさ過ぎるので,考え方に自信ありませんが….

以下,検証がいい加減なところありますので話半分なご参考までに!(というかブラウザのメモリリークを厳密に計測する方法が分からず)

ケースA FileReaderをその都度生成してファイル(5MBのjpeg)を5回選択

document.addEventListener('DOMContentLoaded', function() {
    var input  = document.getElementById('input'),
        _image = document.createElement('image');

    input.addEventListener('change', function(e) {
        var file   = this.files[0],
            reader = new FileReader();

        if ( file.type.indexOf('image') === -1 ) {
            return;
        }

        reader.readAsDataURL(file);
    });
});

ケースB FileReaderを使い回してファイル(5MBのjpeg)を5回選択

document.addEventListener('DOMContentLoaded', function() {
    var input  = document.getElementById('input'),
        _image = document.createElement('image'),
        reader = new FileReader();

    input.addEventListener('change', function(e) {
        var file   = this.files[0];

        if ( file.type.indexOf('image') === -1 ) {
            return;
        }

        reader.readAsDataURL(file);
    });
});

ケースC ケースAと同じコードでファイル(5MBのjpeg)を1回だけ選択

じつはケースAとケースBを選択してから思いついたのですが,1回だけファイルを選択したパターンも比較用に.

結果

Mac版のChromeとFirefoxしか見ていませんが,ケースAでメモリリークらしきものが発生していることが確認できます.

Google Chrome 16 (MemoryはTaskMnagerからTab単位で確認)
ケースA before: 35MB / after: 66MB / result: +31MB
ケースB before: 32MB / after: 43MB / result: +11MB
ケースC before: 33MB / after: 45MB / result: +12MB
Firefox 8 (MacのActivity Monitorで確認)
ケースA before: 131MB / after: 178MB / result: +47MB
ケースB before: 128MB / after: 138MB / result: +10MB
ケースC before: 129MB / after: 143MB / result: +14MB
Firefox 3.6 (MacのActivity Monitorで確認)
ケースA before: 85MB / after: 93MB / result: +8MB
ケースB before: 85MB / after: 94MB / result: +9MB
ケースC before: 85MB / after: 87MB / result: +2MB

今回は5MB程度の大きい画像ファイルで検証しましたが,対象が数十KB程度の小さいファイルになるとここまでは顕在化しません.

readerがreadAsDataURL実行後のresultを抱え込んだまま処分されずに滞留しているから,リーク量がファイルサイズに左右されるんですかね.readAsDataURLを実行しない場合は,ケースAのようなコードでもメモリは膨れあがりません.

Firefox 3.6のケースA〜Cが全体的にあれれ?って感じで何度かやり直したのですが,傾向的には似たようなものでした.Firefoxの3.6当時って恒常的なメモリリークがあった気がするので,単純に操作すれするほど重くなってるだけかもしれませんし,ただの誤差かもしれません.つまりよくわかりません.X-(

ちなみに,1タブあたりのメモリ使用量がみやすいChromeで見たところ,(少なくともChromeは)タブを切り替えるとメモリが開放されるようです.

さらにreader.resultを参照すると…

reader.onload = function() {
    reader.result;
};
reader.readAsDataURL(file);

で,FileReaderがファイルを読み込み終わった後のreader.resultにアクセスすると,これまたメモリが一定量確保されます.dataURLの分ですかね.そいでこれがまた別途メモリリークしてるぽかったり…循環参照がうまく処分さてれないとかでしょうか.

今回FileAPI周りを軽く試すだけのつもりだったところ,大きい画像ファイルで試していたらクラッシュしてしまったので今回の検証に寄り道してしまった次第.

今回は解決的なオチはなしということで....より詳しい情報をご存じの方はご教授いただけたら嬉しいです.