宜しくお願い致します。
javascriptを使っていてふと気になって色々調べたのですが
どうしても納得できない部分がありまして
質問させていただきます。
var hoge = "hogehoge"; // a function foo() { // b var piyo = 'piyopiyo' // c console.log(piyo); // d } foo(); // 正常に動く // e console.log(piyo); 参照エラー // f
とあった場合、
各変数は評価された時点でメモリに割当られるんですよね。
そしてfの時点ではメモリから解放されているんでしょうか。
参考ページ
JavaScriptオブジェクトについては、ガベージコレクタ方式が採用されています。
ガベージコレクタは以下のタイミングでメモリの開放を行います。
- 変数に対し、明示的にnullをセットした時
- 関数の実行終了時
上記以外は任意のタイミング(JavaScriptエンジンによっては、ガベージコレク>タを呼出すメソッドが用意されている)
fの地点でエラーになるのはスコープの問題なのかなって気もしますが。
「メモリに変数が割り当てられているかどうか?」
を確認する術はあるのでしょうか?
Chromeの開発ツールを使って検証したのですがいまいちわかりません。
気になってしまって他の業務がストップしてしまって、、、
どなたかご教授いただけませんでしょうか。
どうぞ宜しくお願い致します。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答3件
0
ベストアンサー
###メモリが解放されるタイミングはわからない。
JavaScript(ECMAScript)の仕様では、いつメモリが解放されるのかは定められていません。GC(ガベージコレクション)がいつ動作するのか、また、そのGCで回収されるのかはJavaScriptエンジンによって異なり、一概には言えません。また、一言でGCと言っても参照カウント方式からM&S方式、世代別GC、インクリメントGCなど多くの方式があり、高度な実装ではそれらが複雑に混在する形になっています。
よって、JavaScriptにおいては、特定の実装について言及していない限り、「○○した時にメモリが解放される」と書かれている物はすべて間違いです。null
をセットしようが、関数が終了しようが、delete
を使おうが、メモリが解放される保証は一切ありません。
GCについて保証される動作はただ一つです。ルート(一番上にあるトップレベルのオブジェクト)から参照を辿って到達できるメモリ領域は絶対に解放されない、ということだけです。
※ WeakMap等を用いた弱参照という特殊な参照でしかたどり着けない領域は解放される場合があります。
###「変数」の寿命と「オブジェクト」の寿命は異なる。
メモリがいつ解放されるのかがわからないといっても、GCが回収できる状態になっているかどうかを把握することは重要です。もし、全てが参照されたままでオブジェクトが増えていった場合、GCが動作しても、メモリが解放されずにいつかは破綻してしまいます。いわゆるメモリリークと言われる問題です。
※ GCがあればメモリリークが起きないわけではありません。メモリリークが起きる不具合のパターンが減るだけです。
さて、プログラムにおいて、メモリ上のデータは生成と破棄を繰り返しています。いつ破棄されるのか(GCに回収されるのか)はわかりませんが、その時点でプログラム上から見えない物になっていますので、コーディング上は破棄された物として扱うべきでしょう。こういった生成してから破棄され(ても問題なくなった状態にな)るまでの期間をここでは寿命といって説明します。
※ ここでの寿命はメモリ上の寿命の事を指しているわけではありませんが、言語によってはメモリ上にデータがあるかどうかを寿命という場合があります。適当な言葉がなかったため今回は「寿命」という言葉を使っていますが、文脈によっては必ずしも正しいとは限りません。
JavaScript
1var x = "abc"; // ①
①のとき変数x
が生成されました。また、文字列"abc"も生成されました。変数x
は先ほど生成された文字列"abc"をを参照するようになりました。
ん、何かおかしいことを言いましたか?一つの文に二つの生成物?そう、ここで重要なのは二つ生成されると言うことです。
※ 数値、真偽値、null、undefinedは少し特殊で、実装上はオブジェクトとして生成されるわけではありません。文字列も少し特殊なのですが、オブジェクトとして生成されるとしています(ただし、プリミティブ型であるため、Objectを継承したオブジェクトではありません)。配列やObjectの方がイメージしやすいかも知れません。
JavaScript
1var x = "abc"; // ① 2x = "012"; // ②
②の時、文字列"012"が生成されました。そして変数x
は先ほど生成された文字列"012"を参照するようになりました。文字列"abc"の寿命が尽きました。
変数x
は相変わらず存在します。しかし、文字列"abc"はもはや参照してくれる物はありません。どのような手段を用いてもその文字列に辿り着くことができないため、やがてGCによって回収されます。これは変数x
の寿命がどうなっているかとは無縁です。
もう何が何だかわからないと思っているかも知れません。重要なのは変数x
とはなんなのかです。JavaScriptにおいて変数とはスコープ(環境)に結びつけられたものです。いきなり何を…と思うかも知れません。スコープは自分が変数のリストを持っています。この変数一つ一つが何かしらのオブジェクトを参照しています(参照先がundefinedの場合もあります)。今トップレベルで書いている上のコードにおいて、変数x
はトップレベルのスコープの変数リストにある一つの変数です。トップレベルが生きている限り、変数x
も生き続けます。
ちょっと関数の場合を見てみましょう。
JavaScript
1function f() { 2 var x = "abc"; // ① 3 return x; // ② 4} 5var y = f(); // ③
このコードにおいて、変数x
は関数f()
のスコープ(関数スコープ)が持っている物です。この関数スコープは関数を実行する度に生成され、その度に変数x
は生成されます(①)。①では同時に文字列"abc"も生成します。文字列"abc"は変数x
を介して関数の戻り値として返っており(②)、変数y
に代入、つまり、変数y
の参照先が文字列"abc"になります(③)。ここで重要なのは、①で生成された文字列"abc"と③で変数y
の参照先となる文字列"abc"が同一であるとうことです。ここでの同一は同じ内容の文字列になるという意味ではなく、メモリ上も全く同じ所になるという意味です。
③のとき、文字列"abc"は変数y
から参照されています。ですので、文字列"abc"はGCで回収されず、まだ生きています。対して、変数x
を見てみましょう。関数スコープは関数が終了した場合、そのスコープを参照している他の生きているスコープがなければ、寿命が尽きます。スコープの寿命が尽きれば、そのスコープが持っていた変数も寿命が尽きます。ということで、②が終わって、③に戻った段階で、変数x
は寿命が尽きていることになります。いずれGCに回収されるでしょう。
おっと、気をつけて欲しいのは変数x
の寿命が尽きただけで、変数x
の参照先の寿命が尽きたというわけでは無いと言うことです。文字列"abc"は既に変数y
からも参照されているため、変数x
がなくなっても、辿ることができる参照はなくなっていません。相変わらず、文字列"abc"は生き続けます。
####【おまけ】クロージャーで変数は寿命が延びる
スコープの寿命についての所で「そのスコープを参照している他の生きているスコープがなければ」と書いてあったのを覚えていますか?何を言っているのだろうと思ったのかも知れませんが、これはクロージャーの仕組みのことです。JavaScriptにはスコープチェーンという仕組みがあり、入れ子になったスコープは親のスコープへの参照を持ちます。クロージャーはスコープごと持ち出す仕組みであるため、この参照もそのまま持ち出すことができます。そうなると、持ち出されたクロージャーの寿命が尽きない限り、参照されたスコープの寿命も尽きず、その中の変数の寿命も尽きません。
JavaScript
1function f() { 2 var x = "abc"; 3 return function() { 4 x += "+"; 5 return x; 6 }; 7} 8var g = f(); 9console.log(g()); // abc+ 10console.log(g()); // abc++ 11console.log(g()); // abc+++
変数x
は関数f()
が終わった後も生き続けます。変数g
が参照している関数のスコープが関数f()
を実行したスコープを参照し続けるからです。なので、その後の呼び出しても、x
は使用し続けることができます。
投稿2017/04/16 07:26
総合スコア21741
0
fの地点でエラーになるのはスコープの問題なのかなって気もしますが。
おっしゃるとおりスコープの問題だと思います。
「メモリに変数が割り当てられているかどうか?」
を確認する術はあるのでしょうか?
質問者さんの疑問が「一般的な話はさておいてChromeのjavascriptエンジン(V8?)はどうなっているのか?」であれば残念ながら自分は知らないのでお答えできません。
GCを持つシステム全般に言える話として捉えさせていただけるなら、「誰からも参照されなくなった変数はいつかガベージコレクターに回収されることは保証されている」ものの、「正確にいつの時点であるかは一般に予測はできない」と思います。少なくとも言語仕様上アクセスできないはずのオブジェクトへの参照がいつまでたっても残るようならばそれはエンジンのバグであることは確かです。よってjavascriptエンジンはおそらく可能な限り速やかに「必要なくなったオブジェクトの参照をしないように」作られているであろうということぐらいしか言えません。
ところで、Chromeデバッガーの画面を拝見するとスコープから出たにもかかわらず変数の値が見えているという点ちょっと面白く感じました。確かなことは分かりませんが、もし本当にスコープが外れても変数の値を見ることができているなら、「デバッガーが実際の値を参照して表示している」のか、「かつて存在していた変数の内容のコピーを表示しているにすぎない」のか自分にはわかりません。どっちの可能性もあると思いますが、なんとなく前者であるような気はします。もしそうならデバッガーを使わないときに比べ、使っている場合は「本来参照されなくなっている変数値がデバッガーによって参照され続けている」ことを意味するのでそのオブジェクトのガベージコレクターによる回収タイミングは遅延する可能性があるかも知れないなぁと感じました。
以上は質問者さんを満足させるような回答でない気がしますが、それはともかく、プログラマーが気にすべきより重要な問題は「実際にメモリーが解放されるタイミング」よりは「いつそのオブジェクトを参照がなくせるか」の方だと思います。
投稿2017/04/16 05:51
編集2017/04/16 06:52総合スコア18404
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

0
test------------
投稿2017/04/27 23:08
総合スコア52
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/04/17 05:12