質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

3回答

15826閲覧

javascriptの変数のメモリへの割当について

yusukexyusuke

総合スコア52

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

5クリップ

投稿2017/04/16 05:19

宜しくお願い致します。

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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yusukexyusuke

2017/04/17 05:12

詳細な回答ありがとうございます! 色々なページや書籍を読み漁って混乱していた部分が腑に落ちた感じです! この回答だけでqiitaの良記事になりそうですね、、、 また宜しくお願い致します。
guest

0

fの地点でエラーになるのはスコープの問題なのかなって気もしますが。

おっしゃるとおりスコープの問題だと思います。

「メモリに変数が割り当てられているかどうか?」

を確認する術はあるのでしょうか?

質問者さんの疑問が「一般的な話はさておいてChromeのjavascriptエンジン(V8?)はどうなっているのか?」であれば残念ながら自分は知らないのでお答えできません。

GCを持つシステム全般に言える話として捉えさせていただけるなら、「誰からも参照されなくなった変数はいつかガベージコレクターに回収されることは保証されている」ものの、「正確にいつの時点であるかは一般に予測はできない」と思います。少なくとも言語仕様上アクセスできないはずのオブジェクトへの参照がいつまでたっても残るようならばそれはエンジンのバグであることは確かです。よってjavascriptエンジンはおそらく可能な限り速やかに「必要なくなったオブジェクトの参照をしないように」作られているであろうということぐらいしか言えません。

ところで、Chromeデバッガーの画面を拝見するとスコープから出たにもかかわらず変数の値が見えているという点ちょっと面白く感じました。確かなことは分かりませんが、もし本当にスコープが外れても変数の値を見ることができているなら、「デバッガーが実際の値を参照して表示している」のか、「かつて存在していた変数の内容のコピーを表示しているにすぎない」のか自分にはわかりません。どっちの可能性もあると思いますが、なんとなく前者であるような気はします。もしそうならデバッガーを使わないときに比べ、使っている場合は「本来参照されなくなっている変数値がデバッガーによって参照され続けている」ことを意味するのでそのオブジェクトのガベージコレクターによる回収タイミングは遅延する可能性があるかも知れないなぁと感じました。

以上は質問者さんを満足させるような回答でない気がしますが、それはともかく、プログラマーが気にすべきより重要な問題は「実際にメモリーが解放されるタイミング」よりは「いつそのオブジェクトを参照がなくせるか」の方だと思います。

投稿2017/04/16 05:51

編集2017/04/16 06:52
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yusukexyusuke

2017/04/16 06:48

早速のご回答ありがとうございます! > 「一般的な話はさておいてChromeのjavascriptエンジン(V8?)はどうなっているのか?」 ブラウザによって仕様が変わりますよね。 試しにIEの開発者ツールでも試してみたらローカル変数piyoは表示されませんでした。 (IEのツールのほうが個人的には見やすかったです。) > プログラマーが気にすべきより重要な問題は「実際にメモリーが解放されるタイミング」よりは「いつそのオブジェクトを参照がなくせるか」の方だと思います。 なるほどです! ありがとうございます!勉強になりました。
KSwordOfHaste

2017/04/16 06:54

Chromeのデバッガの仕様は奇妙ですが本文にも書いた通りちょっと面白く感じました。なんだか変数オブジェクトを不当につかんでいやしないのかと若干不安になりましたw;
KSwordOfHaste

2017/04/16 08:55

なんかChromeデバッガのことを何もしらずにおかしなことをコメントしてしまいましたが、お使いの画面が何かを調べてみるとこれはメモリーのスナップショットの画面ですね・・・とすればpiyoが見えているのは何の不思議もなかったと言えましょう。Chromeデバッガの基本機能をちゃんと知ったうえでコメントすべきでした。失礼しました!
yusukexyusuke

2017/04/16 14:18

検証ありがとうございます! ページをロードし終わった後にスナップショットを撮ったので その時点ではpiyoが見えてるのは変なのかなと思ったのですが、 当然の挙動でしょうか? もしよろしければ、そのように判断された理由を教えていただけないでしょうか!
KSwordOfHaste

2017/04/16 15:04

失礼、この前のコメント「不思議はない」は勘違いです。「(1)スナップショットに変数名が出ている(2)スナップショットを取ったタイミングが変数が生きている時点だった。」と解釈した発言でしたが、(1),(2)のどちらも自分の間違いだと思います。今現在、以下の通りだと解釈してます。(まだ間違ってるかもしれませんが・・・) --- javascriptの中では変数名を指す文字列がエンジンの実行の都合で「文字列オブジェクト」としてどこかに記録されていてスナップショット画面で見えているのはそうしたものも含めてヒープ上に存在する全ての文字列が出ているように思います。piyoという変数が存在すると"piyo"という文字列がjavascriptエンジンの実行の都合で作られると解釈したわけです。実際に自分でvar aaa1="aaa bbb"というような実行文を書いてスナップショットを取ると"aaa1", "aaa bbb"という両方の文字列がスナップショットダンプ上に見えていたので最初は変数名と思ったのですがよく考えれば変数名にしては無秩序に並んでいるので「あ!これ変数ではなく変数名を表す文字列データがヒープにあるんだな」と思ったわけです。
yusukexyusuke

2017/04/16 15:24

ありがとうございます! "piyo" @6277 // ローカル変数 "piyopiyo" @3313 // 文字列オブジェクト のように@xxxxは割り当ててるメモリ領域を指すと予想しておりますが、 ヒープ上にあるということは関数が終了した時点ではGCに回収されてないということですよね。 回収されるタイミングが結局、Chromeのjavascriptエンジン次第とういことになるんですね。
KSwordOfHaste

2017/04/16 16:00

@xxxxははっきりわかりませんがきっとインスタンスの番地的なものを表していると推測します。javaとかでも似たようなことを多分デバッグ目的に便利なようにObject#toStringメソッドがやってますし。 >GCに回収されてないということ ということだと思います。ちなみにスナップショットを採っている時点で「だれからも参照されないけどGCが回収していないだけ」なのか「実は内部的に参照が残っているから残っているのか」は実は結構難しい問題ではあります。自分は変数名も文字列としてエンジンが生成していると予想したわけですが、もしそれが正しいならその文字列がいつまで参照されているかはエンジン内部の仕組みをしらないと予想できないとも言えます。変数に代入されていた文字列については「他で絶対使われてないようなもの」ならば多分参照は切れていてGCが回収していないだけという予測はできそうですけど。こうしたことはなかなか難しい問題だと思います。 ちなみにraccyさんが挙げておられるクロージャーの話はちょうどよい機会だと思うのでよく読んでみるとよいと思います(参照が残るという話に関係します)。スコープ範囲は有限だけどスコープから出ても必要なだけ存在し続ける(参照され続ける)という点ちょっとややこし目の話ですが。
yusukexyusuke

2017/04/17 02:06

ご回答ありがとうございます! raccyさんとKSwordOfHasteさんの回答を読んで 先日読んだ以下のページと結びつきました。 https://books.google.co.jp/books?id=AJf5CgAAQBAJ&pg=PA234&lpg=PA234&dq=javascript+%E5%A4%89%E6%95%B0+%E3%83%92%E3%83%BC%E3%83%97&source=bl&ots=CJPvGrL-KD&sig=4fecoKYr7JOtf8rG8mD9TLinwVg&hl=ja&sa=X&ved=0ahUKEwjdjNjRz6XTAhUGE7wKHaDVDx4Q6AEIQjAE#v=onepage&q=javascript%20%E5%A4%89%E6%95%B0%20%E3%83%92%E3%83%BC%E3%83%97&f=false 変数はスタックされて 文字列オブジェクトはヒープされてるということですかね。 > javascriptにはヒープもガベージコレクションもありません。 と書いてあるので、なんだかまた話がややこしくなりますが、、、 >「だれからも参照されないけどGCが回収していないだけ」 定期的にChromeのJSエンジンが回収してるって記事もあったので、 しばらくしたら自然と回収されるかもですね。。。
KSwordOfHaste

2017/04/17 02:50

> javascriptにはヒープもガベージコレクションもありません。 んー本にはそう書いてありますね。どうも自分と著者さんとではヒープやガベージコレクションという用語の捉え方が食い違っているみたいです。自分ならjavascriptエンジンが「ヒープにオブジェクトを置き、ガベージコレクターを備えている」と表現しそうです。まぁ表現の違いはさておき、ある領域にオブジェクトが管理されていてだれからも参照されなくなったら回収されるというのが肝心な点なのでそこだけぶれなければOKじゃないでしょうか。自分は初心者用の教科書みたとき「わかりやすさのため」あるいは「著者の表現上の文脈が自分の普段使う文脈と違うため」に違和感を感じることはままあると思っているので、肝心な点がなにかが一致してれば多少の違和感は気にしない方です。
yusukexyusuke

2017/05/02 04:14 編集

> ある領域にオブジェクトが管理されていてだれからも参照されなくなったら回収されるというのが肝心な点なのでそこだけぶれなければOKじゃないでしょうか。 そうですね!ついつい正確な正解を求めてしまいがちで、、、 お付き合い頂きありがとうございました!
guest

0

test------------

投稿2017/04/27 23:08

yusukexyusuke

総合スコア52

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問