読者です 読者をやめる 読者になる 読者になる

まりぴよこのブログ

日々の日記。技術ネタでまとまりきってないものの記録、伝わる文章の書き方を練習とか。

おすしで簡単。Javaのメモリ管理をゆるふわ解説。

Javaって参照参照っていうけど、どういう意味なのかイマイチわからんというJava初学者に向けた記事です。

こんなコード、よく書きますよね。

素朴な疑問・・

4行目のsushiに次々と代入してるの、大丈夫なん? Listの方に追加した寿司は次のものに置き換わったりしてないん?

ステップ1

まず寿司の気持ち(?)になって1ループずつ進んでいきましょう。

最初のループ1回目。変数名sushiにnewした値を代入しています。

・・とその前に。Javaにはスタック領域とヒープ領域というのがあり、値をそれぞれ別の領域に管理しています。(ざっくり) 変数を宣言するとスタック領域にその変数が作られると思ってください。 次にnewするなどして、オブジェクトを作った場合、そいつはヒープ領域に作られます。 下の図のように、寿司の本体はかわいい・・じゃなかったでかいので、広い場所でのびのびしてるイメージでお願いします。

変数への代入は、下の図でいうところの紫色の点線の関係を定義している、と考えてください。 スタック領域は狭いので、ヒープ領域の番地のようなものだけ書き込める状態です。

f:id:mpiyok:20140930075056p:plain

ステップ2

次はdishes.add(sushi);です。 ループの外側で定義された変数、dishesに寿司をのせます。

下図のように、dishesの1個目と、sushiはどちらも同じ実体を指しています。

f:id:mpiyok:20140930075238p:plain

ステップ3

1周目のループを抜けました。またSushi sushi = new Sushi();です。 同じ変数名だからといって、1個目の寿司の領域を使ってしまう訳ではありません。 newすると新しいヒープ領域に新しいオブジェクトを作ります。

f:id:mpiyok:20140930075246p:plain

ステップ4

またdishes.add(sushi);です。 ステップ3の変数sushiが指しているものをお皿に載せます。

f:id:mpiyok:20140930075252p:plain

ステップ5

最終ラウンドです。(絵を作るのが面倒になってきたので3人分の寿司しか用意しません) 最後の寿司をnewします。

f:id:mpiyok:20140930075259p:plain

ステップ6

その寿司をお皿に載せます。

f:id:mpiyok:20140930075307p:plain

ステップ5,6の絵は別にいらなかったんじゃないか疑惑が・・・

ステップ7

実は下の絵、ステップ2,4,6の後にそれぞれ発生しています。

Javaでは変数のスコープは結構細かくて、ループ内で宣言した変数はそのループを抜けると無効になります。 ループを抜ける度に、sushi変数は一旦無効になり、もう一度ループの先頭で宣言されなおしてるわけですね。

f:id:mpiyok:20140930075317p:plain

レッツパーティ

肝心のパーティが始まる箇所はコメントで書いてあるだけです。 気が向いたらむしゃむしゃ食べるところを実装してみましょう。

おまけ1(nullを代入したら?)

疑問:変数にnullを代入したらどうなるの?

ステップ 2,4,6の用な状態(変数sushidishes載せられた寿司が同じものを指している時に、sushi = null;したらどうなる?

リストにある方もnullになっちゃうとか⁉︎と思う人もいるかもですが、さっきからの図にチラチラ出てるように、nullは特別な場所に定義されてます。

null代入は、変数の参照先(矢印)がその特別な場所になるだけ。

f:id:mpiyok:20141001074947p:plain

null代入した変数は、寿司を参照しなくなるけど、リストの方はまだ参照してるので、寿司への参照は残ってるため、ガベコレ対象になりません。

おまけ2(ガベージコレクション

ガベコレ? ガベージコレクションのことですね。 参照が無くなったメモリはガベコレゾウさんがやってきて吸い取ってくれます。

さっきの「変数にnull代入したら、なくなっちゃうんじゃ?」不安感の元はこの辺りにあるのかも。。

Javaのヒープ領域で使っているメモリーをお掃除してくれるのが、ガベコレぞうさんです。

f:id:mpiyok:20141001081434j:plain

ガベコレぞうさんは使わなくなったメモリーをお掃除してくれるのですが、プログラマが呼び出すわけではなく Java VM(バーチャルマシーン)の都合により適時実行されます。 プログラマができることは、参照を無くしたり(変数がスコープを外れたり、null代入したり)で 次回ガベコレぞうさんが活動した時に、お掃除してもらえる状態にするところまでで、 実際にいつ消されるかは関与できません。

先ほどの例のように、同じ実体を指している参照があって、片方だけをnullへの参照に切り替えても、 もう一つが生き残っている限りガベコレ対象になりません。

まとめ

  • newするとヒープ領域にオブジェクトが作られる
  • 変数への代入は、ヒープ領域に作られたオブジェクトへ参照の→を定義すること
    • 逆に言うと、newして代入しなくても、ちゃんとオブジェクトは作られる
    • 代入しないということは、プログラムからそのオブジェクトへアクセスすることができないため無意味なので普通しませんが・・
  • ヒープ領域に作られたオブジェクトはプログラマが削除することはできない(Javaの場合)
  • ヒープ領域がお掃除されるのは、ガベコレぞうさんが活動したとき
  • ガベコレぞうさんがお掃除してくれるオブジェクトは、変数からの参照がないもの

すごいざっくりだけど、普通にJavaのプログラム書いてる分にはこのくらいの理解で大丈夫だと思う。。