<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>微風夕語</title><link>http://bone.twbbs.org.tw/blog</link><description>在光之翼的守護下訴說著一段段的英雄傳說。</description><atom:link href="http://bone.twbbs.org.tw/blog/feeds/all.rss.xml" rel="self"></atom:link><lastBuildDate>Thu, 10 May 2012 18:31:00 +0800</lastBuildDate><item><title>[實況教學] 超簡單 Justin / Twitch 實況聊天室擷取法</title><link>http://bone.twbbs.org.tw/blog/misc_2012-05-10-broadcastwithchatroom.rst.html</link><description>&lt;div class="section" id="id1"&gt;
&lt;h2&gt;簡介&lt;/h2&gt;
&lt;p&gt;話說前一陣子家裡換了上傳頻寬比較大的網路，所以偶爾會實況自己玩的遊戲，用的平台是大家常用的 Justin.TV 和 Twitch。&lt;/p&gt;
&lt;p&gt;當然我們在實況遊戲的時候，大部份會希望能夠把當下聊天室的內容也放進實況的畫面，而目前大家的作法大致上可以分成以下這三種：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;直接把 JustinTV / Twitch 網頁上的聊天室拉一個新視窗出來，放在遊戲畫面旁邊&lt;/li&gt;
&lt;li&gt;使用 LimeChat 再加上 NicoLime 跑彈幕&lt;/li&gt;
&lt;li&gt;一樣用 LimeChat，配上 Growl for Windows 來做彈跳通知&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;當然，上述的方式都可以達到一定效果，但我自己用了之後，卻也覺得都有一些缺點在：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;直接拉聊天視窗到畫面上真的很醜，而且保貴的實況畫面都被佔走了，玩 4:3 遊戲的時候還好，但如果玩的是 16:9 的遊戲，就會覺得遊戲畫面被弄到很小。&lt;/li&gt;
&lt;li&gt;NicoLime 跑彈幕的時候真的很耗資源，而且設定上有點繁雜（好唄，其實主因是我不喜歡彈幕 XD）&lt;/li&gt;
&lt;li&gt;Growl 的通知位置只能指定大略的位置，而不能指定到螢幕上詳細的座標&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;由於以上的種種原因，所以我抽空研究了一下，發現原來 Justin.TV 和 Twitch 網頁上的聊天室背後就只是個普通的 IRC 聊天室，所以乾脆就自己寫一個聊天室擷取小程式啦。&lt;/p&gt;
&lt;p&gt;程式的位置在 &lt;a class="reference external" href="https://github.com/brianhsu/IRCBalloon"&gt;GitHub 的專案頁&lt;/a&gt;上，直接捲到最底下就可以下載，只要下載符合自己作業系統的版本後，應該是點兩下那個檔案就可以執行了（如果點兩下不能執行，請先安裝 &lt;a class="reference external" href="http://www.oracle.com/technetwork/java/javase/downloads/jre-7u4-download-1591157.html"&gt;Java 執行環境&lt;/a&gt; ）。&lt;/p&gt;
&lt;p&gt;而這隻程式的使用方式也很簡單，基本上就是切到 Justin 這個頁面，輸入 Justin.TV 或 Twitch 的帳號和密碼，並在視窗下方選擇顯示的方式後，直接按下連線就可以了。&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;在使用 SCFH 的時候，請記得要勾選「顯示多層視窗 / Show Layered Window」，如果是使用 XSplit 的話，則要勾選「Capture Layered Window」這個選項，才能抓到通知視窗。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;img alt="聊天室通知軟體畫面" src="http://i.imgur.com/Btswl.png" /&gt;
&lt;p class="caption"&gt;直接輸入帳號密碼就可以使用&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;至於詳細的介紹和操作方式，可以觀看下面的影片（從 5:30 開始）：&lt;/p&gt;
&lt;div align="center"&gt;
&lt;object bgcolor='#000000' height='300' id='clip_embed_player_flash'
        data='http://zh-tw.twitch.tv/widgets/archive_embed_player.swf'
        type='application/x-shockwave-flash' width='400'&gt;
     &lt;param name='movie' value='http://zh-tw.twitch.tv/widgets/archive_embed_player.swf'&gt;
     &lt;param name='allowScriptAccess' value='always'&gt;
     &lt;param name='allowNetworking' value='all'&gt;
     &lt;param name='allowFullScreen' value='true'&gt;
     &lt;param name='flashvars'
           value='channel=brianhsu&amp;auto_play=false&amp;title=%E8%B6%85%E7%B0%A1%E5%96%AE%E8%81%8A%E5%A4%A9%E5%AE%A4%E6%93%B7%E5%8F%96%E6%95%99%E5%AD%B8+https%3A%2F%2Fgithub.com%2Fbrianhsu%2FIRCBalloon&amp;start_volume=25&amp;hostname=zh-tw.twitch.tv&amp;archive_id=317619536'&gt;
 &lt;/object&gt;
 &lt;/div&gt;&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;軟體下載&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/download/IRCBalloon/IRCBalloon-win32-0.5.jar"&gt;32 位元 Windows XP / Visita / 7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/download/IRCBalloon/IRCBalloon-win64-0.5.jar"&gt;64 位元 Windows XP / Visita / 7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/download/IRCBalloon/IRCBalloon-linux64-0.5.jar"&gt;64 位元 Linux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">墳墓 (Brian Hsu)</dc:creator><pubDate>Thu, 10 May 2012 18:31:00 +0800</pubDate><guid>http://bone.twbbs.org.tw/blog/misc_2012-05-10-broadcastwithchatroom.rst.html</guid><category>實況</category><category>Justin</category><category>Twitch</category><category>教學</category><category>聊天室</category></item><item><title>淺談 C 語言和 Java 的記憶體管理（七）</title><link>http://bone.twbbs.org.tw/blog/programming_2012-04-22-candjavamemory7.rst.html</link><description>&lt;p&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-20-candjavamemory6.rst.html"&gt;&amp;lt;&amp;lt; 上一篇&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="java-non-static"&gt;
&lt;h2&gt;Java 裡的 Non-Static 方法&lt;/h2&gt;
&lt;p&gt;上一篇我們討論了在 Java 裡函式的參數傳遞行為，也提到了在 Java 裡只有 Call By Value 這一種參數傳遞的方式，而你能傳遞的就只有八種 primitive type 的資料，再加上記憶體地址這九種東西而已。&lt;/p&gt;
&lt;p&gt;不過在前一章中，我們討論的也僅止於 static 方法，完全沒提到當呼叫 Instance Method 的時候會發生什麼事情，這次我們就來看一下這個問題。&lt;/p&gt;
&lt;p&gt;首先來看一下下面的程式碼：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setX&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MyObject&lt;/span&gt; &lt;span class="n"&gt;object1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MyObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;MyObject&lt;/span&gt; &lt;span class="n"&gt;object2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MyObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setX&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;object2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setX&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;熟悉 Java 的朋友應該都知道，上面的程式碼印出來的結果會是第一行是 10，第二行是 20。&lt;/p&gt;
&lt;p&gt;一般 Java 教學書上的解釋會是說，Instance Method 所改變到的，會是接收者物件的狀態，而在我們上述的例子中，因為兩個 &lt;tt class="docutils literal"&gt;setX&lt;/tt&gt; 的接收者不同，所以會改到不同物件的狀態。&lt;/p&gt;
&lt;p&gt;但你有沒有好奇過，為什麼明明都是一樣的 &lt;tt class="docutils literal"&gt;x = value&lt;/tt&gt; 程式碼，Java 卻可以區分出來到底要去改哪個物件的 x 呢？&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="instance-method-java"&gt;
&lt;h2&gt;呼叫 Instance Method 時 Java 幫你加的料&lt;/h2&gt;
&lt;p&gt;上面那個問題的答案其實很簡單，因為當你在呼叫 Instance Method 的時候，Java 自動幫你在你傳給 Method 的參數裡面加料了。&lt;/p&gt;
&lt;p&gt;當你寫下 &lt;tt class="docutils literal"&gt;object1.setX(20)&lt;/tt&gt; 的時候，事實上 Java 是丟了 (object1, 20) 這樣的參數到 &lt;tt class="docutils literal"&gt;setX&lt;/tt&gt; 的 Call Stack 上的區域的，而當你在 &lt;tt class="docutils literal"&gt;setX&lt;/tt&gt; 要找原本物件裡面的變數時，就會跑去第一個參數找。&lt;/p&gt;
&lt;p&gt;換句話說，當你在執行上面的程式的時候，執行到 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡第二行的時候，記憶體的狀態如下：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch7/01.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch7/01.png" src="http://bone.twbbs.org.tw/Attachments/ch7/01.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接著我們呼叫了 &lt;tt class="docutils literal"&gt;object1.setX(10)&lt;/tt&gt;，這裡要注意的是你會發現雖然 &lt;tt class="docutils literal"&gt;setX(10)&lt;/tt&gt; 好像只有一個參數，但實際上 Java 會把 object1 內所存放的記憶體地址，也複製一份到 &lt;tt class="docutils literal"&gt;setX()&lt;/tt&gt; 在 Stack 上被配置到的空間，當成 &lt;tt class="docutils literal"&gt;setX()&lt;/tt&gt; 的第一個參數：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch7/02.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch7/02.png" src="http://bone.twbbs.org.tw/Attachments/ch7/02.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接著等到執行 &lt;tt class="docutils literal"&gt;x = value&lt;/tt&gt; 的時候，&lt;tt class="docutils literal"&gt;setX&lt;/tt&gt; 實際上是透過第一個參數的記憶體位置，去找到目前 object1 所指到的物件，並修改其 x 變數的內容：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch7/03.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch7/03.png" src="http://bone.twbbs.org.tw/Attachments/ch7/03.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接著 &lt;tt class="docutils literal"&gt;object1.setX(10)&lt;/tt&gt; 返回後，Stack 上 &lt;tt class="docutils literal"&gt;setX()&lt;/tt&gt; 所屬的區域被消滅：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch7/04.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch7/04.png" src="http://bone.twbbs.org.tw/Attachments/ch7/04.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;而接著執行 object2.setX(20) 時也是同樣的流程，但是一開始我們複製的是 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡的 y 所存的記憶體地址：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch7/05.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch7/05.png" src="http://bone.twbbs.org.tw/Attachments/ch7/05.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch7/06.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch7/06.png" src="http://bone.twbbs.org.tw/Attachments/ch7/06.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;最後 object2.setX(20) 返回，變成像下面這樣，兩個 Instance Method 分別改到不同的物件，所以印出來會是 10 和 20：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch7/07.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch7/07.png" src="http://bone.twbbs.org.tw/Attachments/ch7/07.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;至於其他的部份，其實 non-static 方法和 static 方法，並沒有什麼太大的差別，同樣都是使用 Call By Value 的方式傳遞那八個 primitive type 和記憶體地址而已。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="static-method-non-static-method"&gt;
&lt;h2&gt;為什麼不能在 Static Method 裡呼叫 Non-Static Method&lt;/h2&gt;
&lt;p&gt;有的時候我們在寫一些比較小的 Java 作業（例如解 Project Euler 上面的題目）時，可能會不需要用到物件導向的功能，所以會把所有的 method 都宣告成 static，把 Java 當 C 這類的 Procedure Language 來用，而在這個時候我們有可能會把 static 關鍵字遺忘掉，變成像下面這樣：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;nonStatic&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Hello World&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;nonStatic&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在這個時候，Java 的編譯器就會告訴你：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="go"&gt;brianhsu@USBGentoo ~ $ javac Test.java&lt;/span&gt;
&lt;span class="go"&gt;Test.java:10: non-static method nonStatic() cannot be referenced from a static context&lt;/span&gt;
&lt;span class="go"&gt;        nonStatic();&lt;/span&gt;
&lt;span class="go"&gt;        ^&lt;/span&gt;
&lt;span class="go"&gt;1 error&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;現在你應該了解 Java 會告訴你不能在 static 方法裡呼叫 non-static 方法了吧？因為在這個情況下，你根本沒有「目前的物件的 Reference」的這個東西，可以當做 &lt;tt class="docutils literal"&gt;nonStatic()&lt;/tt&gt; 的第一個參數，所以當然無法呼叫 nonStatic() 這個方法囉。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="autoboxing"&gt;
&lt;h2&gt;還是 AutoBoxing 的問題&lt;/h2&gt;
&lt;p&gt;Java 的函式呼叫到這裡大致上已經差不多了，不過還是要提醒一下，在函式呼叫的時候，Java 的 AutoBoxing 機制也一樣會運作，例如下面的程式碼：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;eatAnything&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;I eat &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;primitiveInt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;eatAnything&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primitiveInt&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;雖然你預期 eatAnything 接收的是指到 Object 類型的物件的 Reference，但由於 Java 具有 AutoBoxing 的機制，所以就算你丟進去的是一個 primitive 的整數，Java 編譯還是會讓你過關，eatAnything 還是會把整數吃下去。&lt;/p&gt;
&lt;p&gt;只是在這個時候，eatAnything 吃下去的實際上已經是 Java 自動幫你建立的 Integer 物件的 Reference，而不是原本的 primitive type 的整數了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;eatAnything&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;I eat &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;primitiveInt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;eatAnything&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primitiveInt&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;這件事看起來好像沒有什麼，但事實上物件的建立比起單純 primitive type 的「複製數值」的動作還是慢上一截，再加上還有 GC 回收你所建立的物件所耗的資源與時間，所以如果你是在寫比較講求執行速度的程式的話，能夠必免用 Boxing 的方式傳 primitive type 的話，就盡量避免吧。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;小結&lt;/h2&gt;
&lt;p&gt;這一篇裡面我們談的主要是 non-static 方法的參數傳遞，你會發現他和一般的 static 方法並沒有什麼太大的不同，都還是使用 Call By Value 的方式來運作，只是 Java 多幫你傳了一個「指到目前的物件的 Reference 裡面的記憶體地址」當做第一個參數而已。&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">墳墓 (Brian Hsu)</dc:creator><pubDate>Sun, 22 Apr 2012 17:24:00 +0800</pubDate><guid>http://bone.twbbs.org.tw/blog/programming_2012-04-22-candjavamemory7.rst.html</guid><category>C</category><category>Java</category><category>記憶體管理</category></item><item><title>Dalvik GC 小記</title><link>http://bone.twbbs.org.tw/blog/programming_2012-04-21-dalvikgc.rst.html</link><description>&lt;p&gt;反正已經追了，就把我之前想到的疑問和從 Dalvik 原始碼裡挖出的答案做一下記錄好了……zzz&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;Dalvik GC 的演算法&lt;/h2&gt;
&lt;p&gt;程式碼位於 &lt;tt class="docutils literal"&gt;vm/alloc/MarkSweep.cpp&lt;/tt&gt;，除了 mark bit 是採用 bitmap 而不是跟隨著 object 本身外，毫無反應，就是個標準的 Mark and Sweep 演算法。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="dalvik-java"&gt;
&lt;h2&gt;Dalvik 裡 Java 物件怎麼放在記憶體中？&lt;/h2&gt;
&lt;p&gt;根據 &lt;tt class="docutils literal"&gt;vm/oo/Object.h&lt;/tt&gt; 的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="mi"&gt;379&lt;/span&gt;     &lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;span class="cm"&gt;380      * Total object size; used when allocating storage on gc heap.  (For&lt;/span&gt;
&lt;span class="cm"&gt;381      * interfaces and abstract classes this will be zero.)&lt;/span&gt;
&lt;span class="cm"&gt;382      */&lt;/span&gt;
&lt;span class="mi"&gt;383&lt;/span&gt;     &lt;span class="n"&gt;size_t&lt;/span&gt;          &lt;span class="n"&gt;objectSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;和 &lt;tt class="docutils literal"&gt;vm/alloc/Alloc.cpp&lt;/tt&gt; 中，&lt;tt class="docutils literal"&gt;dvmAllocObject&lt;/tt&gt; 函式裡的 &lt;tt class="docutils literal"&gt;newObj = &lt;span class="pre"&gt;(Object*)dvmMalloc(clazz-&amp;gt;objectSize,&lt;/span&gt; flags);&lt;/tt&gt; 告訴我們，毫無反應，就是個事先計算好大小的記憶體空間。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="dalvik-gc-prmitive-type-reference"&gt;
&lt;h2&gt;Dalvik GC 是否有區別 prmitive type 和 reference？&lt;/h2&gt;
&lt;p&gt;根據 &lt;tt class="docutils literal"&gt;vm/oo/Object.h&lt;/tt&gt; 裡面告訴我們，一個物件所有是 reference type 的成員變數，都會被調到存放成員變數的區域的最開頭：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="mi"&gt;451&lt;/span&gt;     &lt;span class="cm"&gt;/* instance fields&lt;/span&gt;
&lt;span class="cm"&gt;452      *&lt;/span&gt;
&lt;span class="cm"&gt;453      * These describe the layout of the contents of a DataObject-compatible&lt;/span&gt;
&lt;span class="cm"&gt;454      * Object.  Note that only the fields directly defined by this class&lt;/span&gt;
&lt;span class="cm"&gt;455      * are listed in ifields;  fields defined by a superclass are listed&lt;/span&gt;
&lt;span class="cm"&gt;456      * in the superclass&amp;#39;s ClassObject.ifields.&lt;/span&gt;
&lt;span class="cm"&gt;457      *&lt;/span&gt;
&lt;span class="cm"&gt;458      * All instance fields that refer to objects are guaranteed to be&lt;/span&gt;
&lt;span class="cm"&gt;459      * at the beginning of the field list.  ifieldRefCount specifies&lt;/span&gt;
&lt;span class="cm"&gt;460      * the number of reference fields.&lt;/span&gt;
&lt;span class="cm"&gt;461      */&lt;/span&gt;
&lt;span class="mi"&gt;462&lt;/span&gt;     &lt;span class="kt"&gt;int&lt;/span&gt;             &lt;span class="n"&gt;ifieldCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="mi"&gt;463&lt;/span&gt;     &lt;span class="kt"&gt;int&lt;/span&gt;             &lt;span class="n"&gt;ifieldRefCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// number of fields that are object refs&lt;/span&gt;
&lt;span class="mi"&gt;464&lt;/span&gt;     &lt;span class="n"&gt;InstField&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;      &lt;span class="n"&gt;ifields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="mi"&gt;465&lt;/span&gt;
&lt;span class="mi"&gt;466&lt;/span&gt;     &lt;span class="cm"&gt;/* bitmap of offsets of ifields */&lt;/span&gt;
&lt;span class="mi"&gt;467&lt;/span&gt;     &lt;span class="n"&gt;u4&lt;/span&gt; &lt;span class="n"&gt;refOffsets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;再加上 &lt;tt class="docutils literal"&gt;vm/alloc/MarkSweep.cpp&lt;/tt&gt; 裡的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;span class="cm"&gt; * Scans instance fields.&lt;/span&gt;
&lt;span class="cm"&gt; */&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;scanFields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GcMarkContext&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;refOffsets&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;CLASS_WALK_SUPER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;refOffsets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;refOffsets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refOffsets&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;rshift&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CLZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refOffsets&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CLASS_OFFSET_FROM_CLZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rshift&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dvmGetFieldObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;markObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;refOffsets&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CLASS_HIGH_BIT&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rshift&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClassObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
             &lt;span class="n"&gt;clazz&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
             &lt;span class="n"&gt;clazz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;super&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;InstField&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ifields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ifieldRefCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BYTE_OFFSET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;byteOffset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;JValue&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;markObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;是的，他告訴我們 Dalvik GC 在做 Mark 階段時，只會 Mark Java 物件，而不會去管 primitive type。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;Dalvik GC 釋放記憶體的單位是什麼&lt;/h2&gt;
&lt;p&gt;根據 &lt;tt class="docutils literal"&gt;vm/alloc/HeapSource.cpp&lt;/tt&gt; 裡的 &lt;tt class="docutils literal"&gt;dvmHeapSourceFreeList&lt;/tt&gt; 告訴我們：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="mi"&gt;903&lt;/span&gt; &lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;span class="cm"&gt;904  * Frees the first numPtrs objects in the ptrs list and returns the&lt;/span&gt;
&lt;span class="cm"&gt;905  * amount of reclaimed storage. The list must contain addresses all in&lt;/span&gt;
&lt;span class="cm"&gt;906  * the same mspace, and must be in increasing order. This implies that&lt;/span&gt;
&lt;span class="cm"&gt;907  * there are no duplicates, and no entries are NULL.&lt;/span&gt;
&lt;span class="cm"&gt;908  */&lt;/span&gt;
&lt;span class="mi"&gt;909&lt;/span&gt; &lt;span class="n"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;dvmHeapSourceFreeList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;numPtrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;ptrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;910&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

      &lt;span class="p"&gt;..........&lt;/span&gt;

&lt;span class="mi"&gt;928&lt;/span&gt;             &lt;span class="c1"&gt;// mspace_merge_objects takes two allocated objects, and&lt;/span&gt;
&lt;span class="mi"&gt;929&lt;/span&gt;             &lt;span class="c1"&gt;// if the second immediately follows the first, will merge&lt;/span&gt;
&lt;span class="mi"&gt;930&lt;/span&gt;             &lt;span class="c1"&gt;// them, returning a larger object occupying the same&lt;/span&gt;
&lt;span class="mi"&gt;931&lt;/span&gt;             &lt;span class="c1"&gt;// memory. This is a local operation, and doesn&amp;#39;t require&lt;/span&gt;
&lt;span class="mi"&gt;932&lt;/span&gt;             &lt;span class="c1"&gt;// dlmalloc to manipulate any freelists. It&amp;#39;s pretty&lt;/span&gt;
&lt;span class="mi"&gt;933&lt;/span&gt;             &lt;span class="c1"&gt;// inexpensive compared to free().&lt;/span&gt;
&lt;span class="mi"&gt;934&lt;/span&gt;
&lt;span class="mi"&gt;935&lt;/span&gt;             &lt;span class="c1"&gt;// ptrs is an array of objects all in memory order, and if&lt;/span&gt;
&lt;span class="mi"&gt;936&lt;/span&gt;             &lt;span class="c1"&gt;// client code has been allocating lots of short-lived&lt;/span&gt;
&lt;span class="mi"&gt;937&lt;/span&gt;             &lt;span class="c1"&gt;// objects, this is likely to contain runs of objects all&lt;/span&gt;
&lt;span class="mi"&gt;938&lt;/span&gt;             &lt;span class="c1"&gt;// now garbage, and thus highly amenable to this optimization.&lt;/span&gt;
&lt;span class="mi"&gt;939&lt;/span&gt;
&lt;span class="mi"&gt;940&lt;/span&gt;             &lt;span class="c1"&gt;// Unroll the 0th iteration around the loop below,&lt;/span&gt;
&lt;span class="mi"&gt;941&lt;/span&gt;             &lt;span class="c1"&gt;// countFree ptrs[0] and initializing merged.&lt;/span&gt;

      &lt;span class="p"&gt;........&lt;/span&gt;
&lt;span class="mi"&gt;973&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;由此可知，Dalvik GC 釋放記憶體的單位是一個 Java 物件的整個空間，甚至還會把兩個緊臨的垃圾物件合併起來 free 掉。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="weakreference-softreference-phantomreference"&gt;
&lt;h2&gt;至於 WeakReference / SoftReference 和 PhantomReference&lt;/h2&gt;
&lt;p&gt;Android 的 JavaDoc 早就告訴我們，PhantomReference.get 永遠拿到 null、而 WeakReference.get 和 SoftReference.get 只要本來指到的物件消失後，就只能拿到 null。&lt;/p&gt;
&lt;p&gt;換句話說，當你物件被回收後，還能透過這些東西拿到 reference 的話，此時 Android / Dalvik VM 本身就已經不在正常的狀態了。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;結論&lt;/h2&gt;
&lt;p&gt;我左看右看，都看不出來 Dalvik VM 在正常（也就是符合其設計預期，而且也沒有 bug）的情況下會有以下的狀況：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://fred-zone.blogspot.com/2011/11/android.html"&gt;他會自動將目前 Reference 數量稀少的記憶體回收&lt;/a&gt; (Mark and Sweep 本來就和 Reference 數量無關)&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.plurk.com/p/er0kkl"&gt;單一個 primitive 的整數 instance variable 被回收，不是 bug 而是 Dalvik 的特性&lt;/a&gt; (上面已經看到 Dalvik 的設計上本來就是回收整個物件)&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-14-androidlifecycle.rst.html#comment-497587049"&gt;Dalvik 的 Reference 管理策略，會導至根本就不是 reference type 的整數被回收，或是一個物件被回收後你還能拿到他的 reference。&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;還是再說一次，如果有人能告訴我，我對於 Dalvik VM 的 GC 運作原理的理解是錯的，我會很高興。&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">墳墓 (Brian Hsu)</dc:creator><pubDate>Sat, 21 Apr 2012 20:55:00 +0800</pubDate><guid>http://bone.twbbs.org.tw/blog/programming_2012-04-21-dalvikgc.rst.html</guid><category>Dalvik VM</category><category>GC</category></item><item><title>我所慣用的找尋問題起因之方法</title><link>http://bone.twbbs.org.tw/blog/argument_2012-04-21-findrootcause.rst.html</link><description>&lt;p&gt;今天早上起來看到&lt;a class="reference external" href="http://fred-zone.blogspot.com/2012/04/blog-post.html"&gt;這篇&lt;/a&gt;，我也想也不用對號入座了，反正很明顯是在指我&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-14-androidlifecycle.rst.html#comment-497587049"&gt;之前和 Fred 兄討論 GC 可不可能將變數歸零的事情&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;我只能說，一開始 Fred 兄要我定義，所以我給了 Fred 兄 Java 界公認的權威性定義，一開始的定義是對方要的，所以我提供了最精確的定義，同時，那些問題應該是寫 Java 的人需要具備的 common sense。&lt;/p&gt;
&lt;p&gt;而我也得承認，我真的不知道要怎麼和一個連 F=ma 的公式裡的三個英文字代表什麼意義都不知道的人，去討論火箭動力學相關的問題。&lt;/p&gt;
&lt;p&gt;再者，因為我不是神，所以我所習慣找尋問題起因的方法，是當遇到一個神奇的問題時，用自己所有的一切最基礎的知識以及所知的事實去進行推論與釐清，並且推導可能的原因，然後找出可以支持自己所推導出的結論的「&lt;strong&gt;證據&lt;/strong&gt;」。&lt;/p&gt;
&lt;p&gt;我只想說，在這一切討論的過程中，我很努力地找出各種理論與佐證資料，並加以證明我認為「這不是 GC 的正常行為」的理由，但同時我也很歡迎其他人能提供理論和證明告訴我我對於 Java / Dalvik VM 的所有基本認知是錯的，然而實情卻是到目前為止都沒看到對方提出可信的佐證資料與實證來支持他的「這是 GC 的問題，&lt;a class="reference external" href="http://www.plurk.com/p/er0kkl"&gt;是 Delvik 的特性&lt;/a&gt; 」的反論。&lt;/p&gt;
&lt;p&gt;BTW，這個對話進行到現在，對方連歸零的變數到底是 primitive type 還是 Reference type 都說不清，我真的不知道該怎麼去釐清問題真正發生的原因。&lt;/p&gt;
&lt;p&gt;所以即使是在和 Fred 的討論沒有交集而停止之後，我還&lt;a class="reference external" href="http://stackoverflow.com/questions/10231248/is-is-possible-a-integer-reset-to-zero-due-to-java-android-gc"&gt;特地跑去 StackOverflow 問了這個問題&lt;/a&gt;，並例舉了一大堆我所能思考到與此相關，但我不確定的問題……因為，如果能夠知道我對於 Java / Dalvik GC 的基本認知真的有問題，我會很高興。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我想追求的是可以解釋的事實真相與可以實證的理由，而不是僅止於沒有證明的猜測。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因為自己不知道的事就是不知道，根本沒必要去猜，畢竟世界上有無數種可能的情況，哪有可能猜得完？&lt;/p&gt;
&lt;p&gt;也因為如此，你可以看到我直到現在都仍堅持「我不知道那個變數被歸零的真正原因」，而是提出證據明確地告訴你兩件我所知道的事實而已：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Activity 切換時可能造成外界看起來變數歸零&lt;/li&gt;
&lt;li&gt;以 Java 對於記憶體操作的模型言，GC 將變數歸零不是正常的行為&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;注意，我從來沒說過那個變數不可能被莫名其妙歸零（畢竟我手上沒有出問題的那些 code，而且我還沒有仔細追過 Dalvik VM 是不是有 bug 會造成這樣的情況），我從頭到尾說的都是「那不是 Java / Dalvik 的 GC 的正常行為」，因為那是我所推導出來，並且有理論和證據支持的事情。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;所以即使到現在也是一樣，如果有任何人能告訴我在什麼情況下 Java / Dalvik VM 會出現下面的行為，請務必告訴我：&lt;/strong&gt;&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;GC 回收（重置）一個 primitive 整數&lt;/li&gt;
&lt;li&gt;一個 member field 在物件消滅前先被回收或消滅&lt;/li&gt;
&lt;li&gt;GC 回收了 Integer 物件後你還能保有指到原來的記憶體空間的 Reference&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;因為我真的很想知道是不是我對於 Java / Dalvik 的 GC 認知有誤！&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;強調一次，我不知道的事情就不知道，我不會去猜測，我只能告訴你已知的事實，因為我不是全知的神。&lt;/p&gt;
&lt;p&gt;這門學科會叫 Computer Science 不是白叫的，寫程式的本質是科學，科學講究的是可以驗證的事實，同時科學也要求精確的定義。&lt;/p&gt;
&lt;p&gt;而在程式設計裡面，精確的定義就叫程式語言的 Spec，可以驗證的事實就是該程式語言實作 Compiler / Runtime 的程式碼以及你自己寫的程式碼。&lt;/p&gt;
&lt;p&gt;而我也只能告訴你，在我手上沒有原本出問題的程式碼的情況下，我無法告訴你是不是對方的程式碼本身就有問題，是不是就像 StackOverflow 上其他朋友所講的，可能在隱藏的地方被不小心改到了但卻沒注意到。&lt;/p&gt;
&lt;p&gt;同時我也只能告訴你，就我所知道的 Java Spec 和 GC 的實作方式&lt;a class="footnote-reference" href="#f1" id="id3"&gt;[1]&lt;/a&gt; &lt;a class="footnote-reference" href="#f2" id="id4"&gt;[2]&lt;/a&gt;，GC 把變數歸零不會是正常的行為，而你在一個物件被回收後，也不應該會繼續持有他的 Reference&lt;a class="footnote-reference" href="#f3" id="id5"&gt;[3]&lt;/a&gt;，進而讓你可以存取到原本的那塊記憶體空間內的內容。&lt;strong&gt;而上面這些事情，都可以靠著閱讀 Spec 和追蹤程式碼得到實證&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;如果仔細觀察的話，你應該可以發現在這一切的討論裡，到底是哪一方沒提出任何可信的論證－－到底是誰只講了「我推論後認為這這是 GC 的錯」，卻沒提出任何推論的過程以及理由、論述、證明，沒有告訴你在什麼情況下 GC 會發生這樣的事，在什麼時間點 GC 會認為可以回收那個整數，又是怎麼回收的。&lt;/p&gt;
&lt;p&gt;最後，我只能說，我重看了這一篇「&lt;a class="reference external" href="http://fred-zone.blogspot.com/2012/03/blog-post_26.html"&gt;這是兩碼子事！『犛清問題』與『不是我的問題』！&lt;/a&gt;」後我覺得很諷刺……請問到目前為止，有任何人用了任何方式證明了這件事就是 GC 的問題嗎？好。像。沒。有。&lt;/p&gt;
&lt;p&gt;我從頭到尾講的都是「我不知道真正的原因」，但卻好像有人可以信誓坦坦的告訴你，他推論這就是 GC 的錯，因為不可能有其他原因（&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-14-androidlifecycle.rst.html#comment-498744892"&gt;真的不可能有其他原因嗎？&lt;/a&gt; ），而且還沒提出任何說明與論證告訴你為什麼這是 GC 的錯。&lt;/p&gt;
&lt;p&gt;最後，我只想說，請問一下，這種找問題的起因的態度，和「&lt;strong&gt;不是我的問題&lt;/strong&gt;」有什麼不一樣？&lt;/p&gt;
&lt;p class="rubric"&gt;註腳&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="f1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id3"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;OpenJDK / Dalvik VM / KVM 你都可以找到原始碼，我自己是大學時追過 KVM 的 GC。&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id4"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Dalvik VM 的部份我還沒有追，但 &lt;a class="reference external" href="http://heaven.branda.to/~thinker/GinGin_CGI.py/show_id_doc/385"&gt;Thinker 已經代勞過了&lt;/a&gt;。然後如果你仔細看 Thinker 的追蹤結果，會發現在最一開始 Fred 的那篇文章裡所說的「他會自動將目前 Reference 數量稀少的記憶體回收」這句話本身就已經與 Dalvik VM 的運作事實相違背，&lt;strong&gt;因為在 Dalvik VM 裡面，一個物件所佔的記憶體釋放與否，和 Reference 的數量是完全無關的&lt;/strong&gt;。&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id5"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;即便是你透過 WeakReference / SoftReference 來放他，在物件被回收之後也無法再從 WeakReference 取得指到原本的物件參考了&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">墳墓 (Brian Hsu)</dc:creator><pubDate>Sat, 21 Apr 2012 10:54:00 +0800</pubDate><guid>http://bone.twbbs.org.tw/blog/argument_2012-04-21-findrootcause.rst.html</guid><category>寫程式</category><category>找問題</category></item><item><title>淺談 C 語言和 Java 的記憶體管理（六）</title><link>http://bone.twbbs.org.tw/blog/programming_2012-04-20-candjavamemory6.rst.html</link><description>&lt;p&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-19-candjavamemory5.rst.html"&gt;&amp;lt;&amp;lt; 上一篇&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="java-call-by-reference"&gt;
&lt;h2&gt;獅子的鬃毛－－Java 的物件傳遞是 Call By Reference&lt;/h2&gt;
&lt;blockquote&gt;
傳說拔到獅子的鬃毛，你的 Java 程式行為就會符合預期……&lt;strong&gt;別再相信沒有根據的說法了&lt;/strong&gt;！&lt;/blockquote&gt;
&lt;p&gt;在上一篇當中，我們提到了 C 語言當中，函式呼叫時他的參數存放的位置，以及 C 語言在傳遞參數給函式時的做法，也知道了 C 語言所有的參數傳遞都是 &lt;strong&gt;Call By Value&lt;/strong&gt;，會把傳進函式的值複製一份到 Stack 上該函式的區域。&lt;/p&gt;
&lt;p&gt;接下來的這一篇，我們就來看看在 Java 裡這些事情又是如何運作。&lt;/p&gt;
&lt;p&gt;事實上，如果你去翻網路上的文章，甚至是一些書，他們很可能會在討論 Java 的參數傳遞時，告訴你類似下面的內容：&lt;/p&gt;
&lt;blockquote&gt;
Java 的物件傳遞是 Call By Reference&lt;/blockquote&gt;
&lt;p&gt;拜託，下次當你看到一本寫 Java 的書告訴你 Java 的物件傳遞是 Call By Reference 的時候，請直接把那本書燒了；如果這是教你寫 Java 的老師告訴你的，就請他別再開 Java 的課來誤人子弟了。&lt;/p&gt;
&lt;p&gt;因為在 Programming Language 理論理，&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_reference"&gt;Call By Reference&lt;/a&gt; 有很嚴謹的定義，而 Java 的參數傳遞從頭到尾都只有 &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_value"&gt;Call By Value&lt;/a&gt; 這種方式。&lt;/p&gt;
&lt;p&gt;所以在看這篇文章正文之前，請先跟著我唸五遍：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;從盤谷開天闢地開始，Java 的參數傳遞就只有 Call By Value 一種&lt;/li&gt;
&lt;li&gt;從盤谷開天闢地開始，Java 的參數傳遞就只有 Call By Value 一種&lt;/li&gt;
&lt;li&gt;從盤谷開天闢地開始，Java 的參數傳遞就只有 Call By Value 一種&lt;/li&gt;
&lt;li&gt;從盤谷開天闢地開始，Java 的參數傳遞就只有 Call By Value 一種&lt;/li&gt;
&lt;li&gt;從盤谷開天闢地開始，Java 的參數傳遞就只有 Call By Value 一種&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;很多人會覺得 Java 傳遞參數時的行為很複雜難懂，就是因為誤以為 Java 的參數傳遞有很多種。&lt;/p&gt;
&lt;p&gt;但當你看完這篇之後，就會發現不管你傳的是什麼東西，Java 的行為通通是相同的－－所以請僅記，Java 的參數傳遞方法就只有 Call By Value 這唯一的一種。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;一樣是整數的交換的函式&lt;/h2&gt;
&lt;p&gt;上一篇我們的第一個例子，講的是 C 語言的整數交換的範例，如果把他改寫成 Java 版的話，就會像下面這樣：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SwapInt&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;swap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x = &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;, y = &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果你是從這系列的第一篇看起，一直看到現在，你現在應該知道兩件事了：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;區域變數是存在 Stack 上&lt;/li&gt;
&lt;li&gt;因為 Java 是 Call By Value，所以會把傳入的值複製一份到 swap() 的函式在 Stack 上被配置到的空間。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以現在的你，也應該馬上可以回答出來，這個程式的執行結果是：&lt;/p&gt;
&lt;blockquote&gt;
x = 3, y = 5&lt;/blockquote&gt;
&lt;p&gt;因為如果我們再次把整個執行過程的記憶體裡 Stack 的狀態畫出來的話，會發現他和 C 語言版的 swap() 一模一樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/01.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/01.png" src="http://bone.twbbs.org.tw/Attachments/ch6/01.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/02.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/02.png" src="http://bone.twbbs.org.tw/Attachments/ch6/02.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/03.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/03.png" src="http://bone.twbbs.org.tw/Attachments/ch6/03.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/04.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/04.png" src="http://bone.twbbs.org.tw/Attachments/ch6/04.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/05.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/05.png" src="http://bone.twbbs.org.tw/Attachments/ch6/05.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;如果我就是要更動原本的變數的內容呢？&lt;/h2&gt;
&lt;p&gt;很簡單－－Java 做不到！&lt;/p&gt;
&lt;p&gt;因為 Java 當中的 Reference（實際上就是 Pointer），就只能指到 Heap 上的 Java 物件，而沒有辦法指到位於 Stack 上的變數。&lt;/p&gt;
&lt;p&gt;在這樣的情況下，而 Java 的參數傳遞方式又只有 Call By Value 這一種，所以你永遠不可能更動到 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 的值。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;可是書上都說用物件包起來就好耶？&lt;/h2&gt;
&lt;p&gt;我知道，你一定會跟我說：&lt;/p&gt;
&lt;blockquote&gt;
「嗨，我只要把這個整數用個物件包起來，這樣就可以交換 main() 裡的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 啦！」&lt;/blockquote&gt;
&lt;p&gt;聽起來好像沒錯，所以我們來實作一個把整數包在物件裡的版本好了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MyObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;swap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyObject&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MyObject&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MyObject&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MyObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;MyObject&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MyObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x = &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;, y = &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;你看，現在印出來的就是 &lt;tt class="docutils literal"&gt;x = 5, y = 3&lt;/tt&gt; 啦！我們把兩個整數交換了啊！&lt;/p&gt;
&lt;p&gt;沒錯，他的確印出了 &lt;tt class="docutils literal"&gt;x = 5, y = 3&lt;/tt&gt; 的訊息，可是你有仔細想過你改到的到底是什麼東西嗎？現在就讓我們來認真瞧瞧看這段程式發生了什麼事情。&lt;/p&gt;
&lt;p&gt;首先要回答的就是，當程式一開始跑，執行到 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡的第二行時，記憶體裡的狀態到底是怎樣？&lt;/p&gt;
&lt;p&gt;現在的你應該已經可以很容易回答出來了－－在 Stack 上的 main() 裡面，有著 x 和 y 兩個變數，存的是 Heap 上的兩個 MyObject 物件所在的記憶體地址，所以可以畫出像下面的圖：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/06.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/06.png" src="http://bone.twbbs.org.tw/Attachments/ch6/06.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接著，我們一樣要思考的是 &lt;tt class="docutils literal"&gt;swap(MyObject x, MyObject y)&lt;/tt&gt; 裡的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 是什麼，又放在哪裡？&lt;/p&gt;
&lt;p&gt;現在的我們，已經知道 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 既然不是那八個 primitive type，那他們就一定是存放記憶體地址的 Reference，再加上我們也知道 Java 只有 Call By Value，所以這兩個變數一定是放在 Stack 上！&lt;/p&gt;
&lt;p&gt;有了這樣的認知，就可以畫出像下面這樣的圖：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/07.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/07.png" src="http://bone.twbbs.org.tw/Attachments/ch6/07.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;那現在 swap() 裡的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 到底會被放進什麼呢？還記得 Java 是 Call By Value，而我們傳進去的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 存的又是「存記憶體地址」嗎？&lt;/p&gt;
&lt;p&gt;沒錯！在呼叫 &lt;tt class="docutils literal"&gt;swap(x, y)&lt;/tt&gt; 的時候，會把 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡的 x 和 y 所存的記憶體地址，複製一份到 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 上的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 當中，所以現在我們知道當進入 swap() 函式的時候，記憶體狀態如下所示：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/08.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/08.png" src="http://bone.twbbs.org.tw/Attachments/ch6/08.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接著 &lt;tt class="docutils literal"&gt;int tmp = x.value&lt;/tt&gt; 這一行，會把 swap() 上 x 指到的物件裡的 value 變數的值複製一份到 tmp 裡面：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/09.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/09.png" src="http://bone.twbbs.org.tw/Attachments/ch6/09.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接著執行 &lt;tt class="docutils literal"&gt;x.value = y.value&lt;/tt&gt;，此時的記憶體狀態如下：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/10.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/10.png" src="http://bone.twbbs.org.tw/Attachments/ch6/10.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;之後的 &lt;tt class="docutils literal"&gt;y.value = tmp&lt;/tt&gt; 再把 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 上的 &lt;tt class="docutils literal"&gt;tmp&lt;/tt&gt; 指定給 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 上的 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 所指到的物件當中的 value 成員變數：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/11.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/11.png" src="http://bone.twbbs.org.tw/Attachments/ch6/11.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;最後 swap() 函式返回，Stack 上配置給 swap() 函式用的空間被清空：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/12.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/12.png" src="http://bone.twbbs.org.tw/Attachments/ch6/12.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;這個時候，你會發現如果我們在 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡把 &lt;tt class="docutils literal"&gt;x.value&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y.value&lt;/tt&gt; 把 x 和 y 印出來，就會看起來好像 x 和 y 兩個變數交換了！&lt;/p&gt;
&lt;p&gt;但是請注意一件事－－&lt;/p&gt;
&lt;blockquote&gt;
&lt;strong&gt;在上面的五張圖當中，main() 裡的 ``x`` 和 ``y`` 的內容從來都沒被更改過，一直都分別是 0x00010 和 0x00011！&lt;/strong&gt;&lt;/blockquote&gt;
&lt;p&gt;你交換的是 x 和 y 所指到的物件的狀態，而不像 C 語言的指標那樣，直接改變了 Stack 上的 x 和 y 這兩個變數本身自己的內容！&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="java"&gt;
&lt;h2&gt;Java 傳遞的不是「物件」&lt;/h2&gt;
&lt;p&gt;你可能常常會聽到有人告訴你「Java 在傳遞物件時的行為是……blah blah blah」之類的，但實際上這句話有非常大的問題－－Java 的參數傳遞不會傳物件！&lt;/p&gt;
&lt;p&gt;嚴格來說，在 Java 裡你只能傳以下九種資料給函式（方法）：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;byte&lt;/li&gt;
&lt;li&gt;short&lt;/li&gt;
&lt;li&gt;int&lt;/li&gt;
&lt;li&gt;long&lt;/li&gt;
&lt;li&gt;float&lt;/li&gt;
&lt;li&gt;double&lt;/li&gt;
&lt;li&gt;char&lt;/li&gt;
&lt;li&gt;boolean&lt;/li&gt;
&lt;li&gt;記憶體地址（也就是 Java 當中的 Reference 的「值 / Value」）&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;這裡最主要的觀念，是 Java 裡的 Reference 實際上就只是一個存放記憶體地址的東西，他和一般的整數並沒有什麼不同，都是一種「數值」，而我們在做函式傳遞時，是把那個「數值」複製一份到新的地方。&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;重申一次！Java 不會傳遞物件！而且參數的傳遞方式就只有 Call By Value 這一種！也就是當你把上面的九種資料傳給某個函式時，他們的內容都會被複製一份到 Stack 上該函式所屬的空間當中。&lt;/p&gt;
&lt;p&gt;接下來，我們來看看下面的程式會發生什麼事：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MyObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyObject&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MyObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MyObject&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MyObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 請問這一行會印出什麼&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x = &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;同樣的，要回答這個問題的關鍵，是 &lt;tt class="docutils literal"&gt;x = new MyObject(10)&lt;/tt&gt; 這行，到底在記憶體裡動了什麼手腳。&lt;/p&gt;
&lt;p&gt;而現在的我們已經知道：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;test&lt;/tt&gt; 的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 是放在 Stack 屬於 test() 自己的區塊上，存的是記憶體地址&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;new MyObject(10)&lt;/tt&gt; 會在 Heap 上產生一個新的物件，並返回他的記憶體地址&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以如果我們把整個流程畫出來，就會如下所示：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/13.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/13.png" src="http://bone.twbbs.org.tw/Attachments/ch6/13.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/14.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/14.png" src="http://bone.twbbs.org.tw/Attachments/ch6/14.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/15.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/15.png" src="http://bone.twbbs.org.tw/Attachments/ch6/15.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/16.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/16.png" src="http://bone.twbbs.org.tw/Attachments/ch6/16.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;現在，你可以知道為什麼印出來的會是 x = 3 而不是 x = 10 了吧？&lt;/p&gt;
&lt;p&gt;同時你也應該會發現，這樣子做的話，實際上會在 Heap 上產生一個垃圾物件，因為只要你一離開了 test() 函式，就沒有人可以尋著任何 Reference 拿到你在 test() 裡 new 出來的 MyObject 囉。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;傳 Java 陣列時發生什麼事&lt;/h2&gt;
&lt;div class="section" id="id6"&gt;
&lt;h3&gt;Java 的陣列到底是什麼&lt;/h3&gt;
&lt;p&gt;和 C 語言一樣，當我們在看 Java 中丟一個陣列給函式會發生什麼事的時候，要先了解 Java 的陣列是什麼才行。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;注意！問題來了！上面的 Java 程式裡的 x 是什麼？！&lt;/p&gt;
&lt;blockquote&gt;
「我知道，是一個長度為 3 的整數陣列！」&lt;/blockquote&gt;
&lt;p&gt;錯！錯！錯！老師在講你都沒有在聽嘛－－我們之前就看過 Java 對於「物件」這兩個字的定義了：&lt;/p&gt;
&lt;blockquote&gt;
An object is a class instance or an array.&lt;/blockquote&gt;
&lt;p&gt;沒錯，在 Java 中陣列是個物件，而物件是存在 Heap 上的，然後在 Java 裡要操作 Heap 上的物件就只能依靠名叫 Reference 的 Pointer，所以這個問題的標準答案應該是：&lt;/p&gt;
&lt;blockquote&gt;
「x 是一個指向長度為 3 的整數陣列的 Reference」&lt;/blockquote&gt;
&lt;p&gt;換句話說，在這個程式裡，我們的記憶體長相如下：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/17.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/17.png" src="http://bone.twbbs.org.tw/Attachments/ch6/17.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h3&gt;傳陣列到函式裡&lt;/h3&gt;
&lt;p&gt;只要你弄懂了上面所說的 Java 的陣列其實是物件，和上述的程式碼裡的 x 實際上是 Reference 後，其參數的傳遞好像就沒啥好講的了，因為前面都提過了。&lt;/p&gt;
&lt;p&gt;例如當你看到以下的程式碼：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addByOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;

        &lt;span class="n"&gt;addByOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[0] = &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[1] = &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[2] = &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;]);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;應該可以馬上一步一步畫出記憶體裡的狀態圖了：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/18.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/18.png" src="http://bone.twbbs.org.tw/Attachments/ch6/18.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/19.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/19.png" src="http://bone.twbbs.org.tw/Attachments/ch6/19.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/20.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/20.png" src="http://bone.twbbs.org.tw/Attachments/ch6/20.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/21.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/21.png" src="http://bone.twbbs.org.tw/Attachments/ch6/21.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/22.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/22.png" src="http://bone.twbbs.org.tw/Attachments/ch6/22.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch6/23.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch6/23.png" src="http://bone.twbbs.org.tw/Attachments/ch6/23.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;然後你也應該很容易就回答得出來，這個程式印出來的會是以下的結果了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;x[0] = 2
x[1] = 3
x[2] = 4
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id8"&gt;
&lt;h2&gt;小結&lt;/h2&gt;
&lt;p&gt;在這一篇裡面，我們提到了 Java 的參數傳遞方式，以對於 Java 的參數傳遞有多種模式這個常見的誤解。&lt;/p&gt;
&lt;p&gt;而要了解這一切，最重要的一個概念就是 Java 只有 Call By Value，並且你能傳的就只有八種 Primitive Type 再加上記憶體位址這九種資料而已。&lt;/p&gt;
&lt;p&gt;然而在這篇文章當中，我們討論的都僅止於 static method 的參數傳遞而已，如果是 instance method 的話 Java 的行為又會是如何呢？這會是我們下一篇討論的重點。&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">墳墓 (Brian Hsu)</dc:creator><pubDate>Fri, 20 Apr 2012 20:03:00 +0800</pubDate><guid>http://bone.twbbs.org.tw/blog/programming_2012-04-20-candjavamemory6.rst.html</guid><category>C</category><category>Java</category><category>記憶體管理</category></item><item><title>淺談 C 語言和 Java 的記憶體管理（五）</title><link>http://bone.twbbs.org.tw/blog/programming_2012-04-19-candjavamemory5.rst.html</link><description>&lt;div class="section" id="id1"&gt;
&lt;h2&gt;目錄&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-18-candjavamemory4.rst.html"&gt;&amp;lt;&amp;lt; 上一篇&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="c"&gt;
&lt;h2&gt;C 語言程式設計期中考－－整數交換&lt;/h2&gt;
&lt;p&gt;話說如果你曾經在大學修過 C 語言相關課程的話，應該有很大的機會在課堂上或期中考中，看到下面這個考題：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 請問下面這一行程式碼會印出什麼&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x = %d, y = %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;當你把這個問題拿去給任何一個稍具經驗的 C Programmer 的話，他會立刻回答你印出來的一定是：&lt;/p&gt;
&lt;blockquote&gt;
x = 3, y = 5&lt;/blockquote&gt;
&lt;p&gt;而且這個 Programmer 甚至可以不用去看你的 &lt;tt class="docutils literal"&gt;swap&lt;/tt&gt; 函式裡寫了什麼，就告訴你答案一定是這樣。&lt;/p&gt;
&lt;p&gt;為什麼？我們不是在 swap 裡面把 x 和 y 的變數交換了嗎？為什麼 main() 裡印出來的卻還是原來的 3 和 5 呢？&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="c-call-by-value"&gt;
&lt;h2&gt;C 語言函式參數的存放位置與 Call By Value&lt;/h2&gt;
&lt;p&gt;如你問那個 C Programmer 為什麼會是這樣，他可能會回答你「因為傳進去函式的參數與 main 裡面的變數無關」……但這句話到底是什麼意思呢？&lt;/p&gt;
&lt;p&gt;要回答這個問題，仍然要回到一切的原點－－你傳進去的函式的參數到底存在記憶體的哪裡，而你的每一行 C 語言敘述，操作的又是記憶體的哪裡。&lt;/p&gt;
&lt;p&gt;針對第一點，在 C 語言和 Java 裡面，你函式的參數，都被放在你呼叫函式時被配置給那個函式的 Stack 空間上，還記得區域變數與是被放在 Stack 上嗎？沒錯－－在 C 語言裡面，函式的參數和區域變數其實是相同的東西，所以你的函式參數也會在函式執行完之後消滅。&lt;/p&gt;
&lt;p&gt;有了上面的認知，我們現在知道當我們在 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡呼叫 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 時，我們的 Call Stack 應該會長得像下面一樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/01.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/01.png" src="http://bone.twbbs.org.tw/Attachments/ch5/01.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;現在問題來了，當我們在 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡面呼叫 &lt;tt class="docutils literal"&gt;swap(x, y);&lt;/tt&gt; 的時候，一開始到底發生了什麼事，Stack 上屬於 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 函式的 x 和 y 又會是什麼呢？&lt;/p&gt;
&lt;p&gt;答案很簡單－－C 語言會把你丟進去給參數的「值」複製一份到現在 Stack 上新的 x 和 y－－這種在函式呼叫的時候把傳進去的值複製一份的做法，就叫做 &lt;a class="reference external" href="http://c2.com/cgi/wiki?CallByValue"&gt;Call by value&lt;/a&gt;！&lt;/p&gt;
&lt;blockquote&gt;
&lt;strong&gt;C 語言裡面所有的參數傳遞都是 Call by value&lt;/strong&gt;！&lt;/blockquote&gt;
&lt;p&gt;接下來，既然當你在 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡呼叫 &lt;tt class="docutils literal"&gt;swap(x, y)&lt;/tt&gt; 的那個時間點，x 和 y 分別是 3 和 5，所以現在你的 Stack 會長得像下面一樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/02.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/02.png" src="http://bone.twbbs.org.tw/Attachments/ch5/02.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接下來要注意的是，&lt;strong&gt;你在 C 語言函式裡存取的參數，都是 Stack 上屬於你的函式區塊的那一個&lt;/strong&gt; 。&lt;/p&gt;
&lt;p&gt;有了這樣的認知，我們就知道接下來所有的動作，都不會影響到 Stack 上屬於 main() 函式的記憶體區塊了。&lt;/p&gt;
&lt;p&gt;接下來就一步一步來看記憶體中的變化吧！當我們執行到 &lt;tt class="docutils literal"&gt;int tmp = x;&lt;/tt&gt; 的時候，會把 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 上面的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 存放的值複製一份到 &lt;tt class="docutils literal"&gt;tmp&lt;/tt&gt; 當中：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/03.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/03.png" src="http://bone.twbbs.org.tw/Attachments/ch5/03.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;然後 &lt;tt class="docutils literal"&gt;x = y&lt;/tt&gt; 時，會把 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 上 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 的值複製到 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 上的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 當中：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/04.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/04.png" src="http://bone.twbbs.org.tw/Attachments/ch5/04.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;之後的 &lt;tt class="docutils literal"&gt;y = tmp&lt;/tt&gt; 則會把 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 上的 &lt;tt class="docutils literal"&gt;tmp&lt;/tt&gt; 的值複製到 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 上的 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 當中：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/05.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/05.png" src="http://bone.twbbs.org.tw/Attachments/ch5/05.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;最後，當 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 函式返回之後，Call Stack 上配置給 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 函式的空間就被消滅了，所以現在 Call Stack 上只剩下 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 函式裡的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 變數了，而且他們&lt;strong&gt;從頭到尾都沒被動過&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/06.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/06.png" src="http://bone.twbbs.org.tw/Attachments/ch5/06.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;現在，你應該知道為什麼這樣的 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 函式並不能做兩個數字的交換了吧？因為你改到的，都是在 Stack 上屬於 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 的變數，而不是 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡面的變數。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;如果我就是要更動原來的變數呢？&lt;/h2&gt;
&lt;p&gt;但在有的情況之下，我們想要的結果就是 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 兩個東西被交換，這個時候應該要怎麼辦呢？&lt;/p&gt;
&lt;p&gt;嗯……如果我們可以想辦法直接操作 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 這兩塊記憶體好像就可以做到耶……&lt;/p&gt;
&lt;p&gt;沒錯！這個時候就是 Pointer 派上用場的時候了！&lt;/p&gt;
&lt;p&gt;還記得我們之前講過，Pointer 可以存記憶體的地址，然後我們可以利用 Pointer 來操作被指到的記憶體的內容嗎？如果我們能想辦法找到 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 的記憶體地址，那就可以在函式裡直接操作他！&lt;/p&gt;
&lt;p&gt;有了這樣的觀念之後，我們就可以把我們的 swap 函式改掉，改成用 Pointer 來寫－－&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;#include &amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 請問下面這一行會印出什麼&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x = %d, y = %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;這邊你可能會問，現在 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 的參數是 Pointer 耶，那他到底算什麼？答案是－－毫無反應，就是個存放記憶體位址的空間。&lt;/p&gt;
&lt;p&gt;所以現在你呼叫 swap() 的時候，你的 Call Stack 長得還是像下面一樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/07.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/07.png" src="http://bone.twbbs.org.tw/Attachments/ch5/07.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;現在我們再來看看呼叫 &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;swap(&amp;amp;x,&lt;/span&gt; &amp;amp;y);&lt;/tt&gt; 這行時，到底發生了什麼事。&lt;/p&gt;
&lt;p&gt;首先我們已經知道 C 語言的函式參數都是 Call By Value，所以我們要先知道我們到底傳了什麼進去，而 &lt;tt class="docutils literal"&gt;&amp;amp;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;&amp;amp;y&lt;/tt&gt; 又是什麼東西。&lt;/p&gt;
&lt;p&gt;這個問題的答案也很簡單，當你在 C 語言的變數前加上 &amp;amp; 號時，代表你要取得那個變數的「記憶體地址」，也就是說，現在我們傳進去給 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 的，是 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 的記憶體位置，假設他們分別位於 0x00010 和 0x00011 的位置的話，那就是傳入 0x00010 和 0x00011。&lt;/p&gt;
&lt;p&gt;所以現在我們的記憶體長得像下面這樣，由於 swap() 裡的 x 和 y 是 Pointer，所以我們順便幫他們畫條箭頭到他們所指到的記憶體區塊：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/08.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/08.png" src="http://bone.twbbs.org.tw/Attachments/ch5/08.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接著因為執行到 &lt;tt class="docutils literal"&gt;int tmp = *x&lt;/tt&gt;，因為我們已經知道在一個 Pointer 變數前加上 * 號是指尋著 Pointer 找到他所指的那塊記憶體的「值」，所以現在 &lt;tt class="docutils literal"&gt;tmp&lt;/tt&gt; 會被改成 3：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/09.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/09.png" src="http://bone.twbbs.org.tw/Attachments/ch5/09.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;下一步的 &lt;tt class="docutils literal"&gt;*x = *y&lt;/tt&gt; 這邊，則會把 x 這個 Pointer 指到的記憶體的內容，改成 y 這個 Pointer 指到的記憶體位置的內容：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/10.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/10.png" src="http://bone.twbbs.org.tw/Attachments/ch5/10.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;最後的 &lt;tt class="docutils literal"&gt;*y = tmp&lt;/tt&gt; 則會把 y 這個 Pointer 指到的記憶體內容，改成 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 上面的 tmp 的值，所以變成這樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/11.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/11.png" src="http://bone.twbbs.org.tw/Attachments/ch5/11.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;最後 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 返回，所以 Stack 上屬於 &lt;tt class="docutils literal"&gt;swap()&lt;/tt&gt; 函式的所有東西都被清空：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/12.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/12.png" src="http://bone.twbbs.org.tw/Attachments/ch5/12.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;瞧！現在我們 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 函式裡的 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; 真的被交換了耶！所以如果執行我們新版的程式的話，印出來的就會是符合我們預期的：&lt;/p&gt;
&lt;blockquote&gt;
x = 5, y = 3&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;C 語言當中的陣列&lt;/h2&gt;
&lt;div class="section" id="id5"&gt;
&lt;h3&gt;基本的陣列宣告與配置&lt;/h3&gt;
&lt;p&gt;講到了這邊，我們終於可以開始討論另一個有趣的問題了－－C 語言的陣列到底是什麼，當我們把他傳進一個函式的時候，又會發生什麼事呢？&lt;/p&gt;
&lt;p&gt;舉例而言，我們可能很習慣寫出像下面的 C 語言程式碼來操作一個整數陣列：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[1] is %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[1] is %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;也知道最後的結果會是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="go"&gt;x[1] is 2&lt;/span&gt;
&lt;span class="go"&gt;x[1] is 10&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;但問題來了－－這個 x 在記憶體裡到底是什麼東西呢？這個問題如果你去翻教科書，標準的答案就是「一塊可以存下 3 個整數的連續記憶體空間」，同樣的，既然這個陣列是被宣告在 main() 函式裡，那他就會存放在 Stack 上配置給 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 的空間。&lt;/p&gt;
&lt;p&gt;所以一開始我們的記憶體長這樣，印出 x[1] 的時候會是 2：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/13.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/13.png" src="http://bone.twbbs.org.tw/Attachments/ch5/13.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接著 &lt;tt class="docutils literal"&gt;x[1] = 10&lt;/tt&gt; 會把 10 放到陣列的第二個位置，所以印出來會是 &lt;tt class="docutils literal"&gt;x[1] is 10&lt;/tt&gt;，而現在 Stack 上則長得像下面一樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/14.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/14.png" src="http://bone.twbbs.org.tw/Attachments/ch5/14.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;以上就是我們習慣的陣列的使用方法，但實際上 C 語言對於型態是陣列的變數的處理，有一些需要注意的地方，下面我們就來探討這個問題。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="x"&gt;
&lt;h3&gt;當你寫下 x 的時候到底是啥意思&lt;/h3&gt;
&lt;p&gt;不知道你有沒有不小心寫過像下面這樣的程式，本來是要取得陣列裡面某一個元素的值，但卻忘記加上陣列的索引了呢？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;#include &amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// 本來是想寫 int y = x[0] 的&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;y = %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;這個時候如果你是在 Linux 下 GCC 去編譯他的話，會跑出奇怪的警告，而如果你執行他的話，則會跑出一個奇怪的數字：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="go"&gt;brianhsu@USBGentoo ~ $ gcc test2.c&lt;/span&gt;
&lt;span class="go"&gt;test2.c: In function 「main」:&lt;/span&gt;
&lt;span class="go"&gt;test2.c:6:13: 警告：初始化將指標賦給整數，未作類型轉換&lt;/span&gt;

&lt;span class="go"&gt;brianhsu@USBGentoo ~ $ ./a.out&lt;/span&gt;
&lt;span class="go"&gt;y = 1754525472&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;這是為什麼呢？關鍵就在於 GCC 給我們的警告「初始化將指標賦給整數，未作類型轉換」這句話－－看到「&lt;strong&gt;指標 (Pointer）&lt;/strong&gt;」這個關鍵字了嗎？&lt;/p&gt;
&lt;p&gt;咦？！我們的程式裡沒有用到指標啊！為什麼會出現和指標相關的警告呢？&lt;/p&gt;
&lt;p&gt;這是因為，在 C 語言當中，當你宣告這個 x 變數是個陣列後，以後每當你在程式碼裡寫 &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; 的時候，事實上他會被視為（精確的說法是 evaluate）成「指向陣列開頭的記憶體地址」……而記憶體地址換句話說，就是 Pointer。&lt;/p&gt;
&lt;p&gt;而當你寫 &lt;tt class="docutils literal"&gt;x[1]&lt;/tt&gt; 的時候，實際上指的就是「取得從陣列開頭的記憶體位置再加上一個整數位移後的地址裡面的值」，也就是說，如果你現在有一個指到陣列開頭的指標 p，那麼 &lt;tt class="docutils literal"&gt;*(p+1)&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;x[1]&lt;/tt&gt; 實際上是等價的東西。&lt;/p&gt;
&lt;p&gt;由於上面的特性，你可以在 C 語言裡面做一些很好玩的事情，例如下面這樣：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;#include &amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;  &lt;span class="c1"&gt;// 宣告一個陣列&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// 將一個 Pointer 指到陣列開頭&lt;/span&gt;

    &lt;span class="c1"&gt;// 用 Pointer 的方式使用陣列&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[0] = %d &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[1] = %d &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[2] = %d &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// 用陣列的方式使用 Pointer&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;p[0] = %d &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;p[1] = %d &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;p[2] = %d &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;將陣列當作參數&lt;/h2&gt;
&lt;p&gt;上面我們已經看過 C 語言陣列的特性了，現在就來看一下，如果你把 C 語言的陣列傳到一個函式裡的時候，會發生什麼事。&lt;/p&gt;
&lt;p&gt;這次我們的程式碼如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;#include &amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addByOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;  &lt;span class="c1"&gt;// 宣告一個陣列&lt;/span&gt;

    &lt;span class="n"&gt;addByOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[0] = %d &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[1] = %d &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;x[2] = %d &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;現在我們已經知道 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡的 x 陣列是被放在 Stack 上屬於 main() 的記憶體裡的一塊連續的空間了，所以剩下來的就是……&lt;tt class="docutils literal"&gt;p&lt;/tt&gt; 是存在哪裡，存的又是些什麼東西呢？&lt;/p&gt;
&lt;p&gt;還記得我們強調過 &lt;strong&gt;C 語言只有 Call By Value&lt;/strong&gt; 嗎？所以事實上 p 和其他所有函式的參數一樣，被存在 addByOne 這個函式被分配到的 Stack 空間上，所以當我們呼叫 addByOne 的時候，記憶體會長得像這樣子：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/15.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/15.png" src="http://bone.twbbs.org.tw/Attachments/ch5/15.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;現在問題來了，addByOne 裡的 &lt;tt class="docutils literal"&gt;p&lt;/tt&gt; 存的到底是啥呢？我們在呼叫這個函數時寫的是 &lt;tt class="docutils literal"&gt;addByOne(x)&lt;/tt&gt; 然後 x 是一個陣列，我們又知道單寫 x 時會被視為記憶體位址……沒錯！就是 x 陣列開頭的記憶體位址！&lt;/p&gt;
&lt;p&gt;假設現在 x 是位在 0x00010 這個位置的話，那 p 裡面就會存 0x00010 這個數值，而我們知道存記憶體位置的東西本質上就是 Pointer，所以可以幫他畫條線，變成像下面這樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/16.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/16.png" src="http://bone.twbbs.org.tw/Attachments/ch5/16.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接著進到 addByOne 函式裡面，我們已經知道 &lt;tt class="docutils literal"&gt;p[0]&lt;/tt&gt; 實際上的翻譯就是 &lt;tt class="docutils literal"&gt;*(p+0)&lt;/tt&gt;，所以是更改 p 所指到的記憶體位置的內容，所以會變成這樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/17.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/17.png" src="http://bone.twbbs.org.tw/Attachments/ch5/17.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;依此類推執行 &lt;tt class="docutils literal"&gt;p[1] = p[1] + 1&lt;/tt&gt; 和 &lt;tt class="docutils literal"&gt;p[2] = p[2] + 1&lt;/tt&gt; 時的記憶體狀態會像下面一樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/18.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/18.png" src="http://bone.twbbs.org.tw/Attachments/ch5/18.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/19.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/19.png" src="http://bone.twbbs.org.tw/Attachments/ch5/19.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;最後 addByOne() 函式返回後，Stack 上關於 addByOne() 的東西就會消滅掉，而我們在 main() 裡印出的陣列的值的時候，就會發現原來放在 main() 裡的陣列內容被改變囉。&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch5/20.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch5/20.png" src="http://bone.twbbs.org.tw/Attachments/ch5/20.png" style="width: 300px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h2&gt;小結&lt;/h2&gt;
&lt;p&gt;這次我們看了在 C 語言裡面，傳進函式的參數到底是放在哪裡。&lt;/p&gt;
&lt;p&gt;同時我們也知道了 C 語言在做函式呼叫的時候，參數一律是用 Call By Value 的方法，將傳入的東西的值複製一份到自己的 Stack 上。&lt;/p&gt;
&lt;p&gt;最後，我們還討論到了 C 語言裡的陣列在記憶體中是如何表示的，以及在函式呼叫時他所具有的特性。&lt;/p&gt;
&lt;p&gt;然而這些東西，在 Java 裡又會是如何呢？這就是我們下一次的主題。&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">墳墓 (Brian Hsu)</dc:creator><pubDate>Thu, 19 Apr 2012 18:49:00 +0800</pubDate><guid>http://bone.twbbs.org.tw/blog/programming_2012-04-19-candjavamemory5.rst.html</guid><category>C</category><category>Java</category><category>記憶體管理</category></item><item><title>淺談 C 語言和 Java 的記憶體管理（四）</title><link>http://bone.twbbs.org.tw/blog/programming_2012-04-18-candjavamemory4.rst.html</link><description>&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;2012-04-19 更新影片&lt;/p&gt;
&lt;p&gt;感謝 OSDC.tw 2012 的工作人員，今年剛好有一場講的就是 Java 的 GC，而且影片已經出來了。當你看完這篇之後，也可以看看下面這個影片，是非常好的簡介，從很基本的 Garbage Collection 介紹起到目前 OpenJDK 的 GC 現況都有。&lt;/p&gt;
&lt;div class="youtube" align="center"&gt;&lt;iframe width="480" height="320" src="http://www.youtube.com/embed/mumvD0r7IH4" frameborder="0"&gt;&lt;/iframe&gt;&lt;div class="last"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;目錄&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-16-candjavamemory.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（一）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory2.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（二）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory3.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（三）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;淺談 C 語言和 Java 的記憶體管理（四）&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="java"&gt;
&lt;h2&gt;為什麼 Java 不用歸還記憶體&lt;/h2&gt;
&lt;p&gt;上一篇我們講到了我們所 new 出來的 Java 的物件，實際上都存放在 Heap 上，並且可以透過名叫 Reference 但實際上就是 Pointer 的變數去操作這些 Java 物件。&lt;/p&gt;
&lt;p&gt;而我們在更前面一點也提到，當你在 C 語言裡面使用 malloc 向 C 語言的 runtime 要到 Heap 上的空間後，一定要用 free 來把空間還回去，否則會造成記憶體洩漏的情況。&lt;/p&gt;
&lt;p&gt;但你應該也會注意到，我們在寫 Java 程式的時候，沒有在釋放記憶體的，好像頂多就是寫個 &lt;tt class="docutils literal"&gt;myObject = null&lt;/tt&gt; 這樣的指令，而書上就會告訴你這樣就可以把 myObject 用的記憶體回收掉的樣子。&lt;/p&gt;
&lt;p&gt;事實上，&lt;tt class="docutils literal"&gt;myObject = null&lt;/tt&gt; 是在釋放記憶體的這種說法是有點問題的，我們等下會看到。&lt;/p&gt;
&lt;p&gt;現在你需要有的觀念是，我們在 Java 中向 JVM 要回來的記憶體空間，是由 JVM 自己決定要不要還回去，而不是由程式設計師決定！這是很重要的觀念－－換句話說，在寫 Java 程式的時候，你永遠無法切確地指出在 Heap 上你的物件所佔的區塊，到底是在哪個時間底被釋放。&lt;/p&gt;
&lt;p&gt;而 Java 用來管理與決定這些從 Heap 上要來的空間是不是要被還回去的方法，是一個叫做 Garbage Collection 的機制。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="garbage-collection"&gt;
&lt;h2&gt;什麼是 Garbage Collection&lt;/h2&gt;
&lt;p&gt;Garbage Collection，翻成中文就是「垃圾收集」，通常簡稱 GC。&lt;/p&gt;
&lt;p&gt;他就有點像是 Heap 上的清道夫一樣，會不時地去尋視看看現在的 Heap 裡面，有沒有不可能再被你的程式存取到的物件－－如果他發現了 Heap 上面有你的程式再也拿不到的物件，那他就會認為那個物件是佔據記憶體的垃圾，可以把他拿去回收，配置給其他人用。&lt;/p&gt;
&lt;p&gt;至於 Java 裡 GC 的機制是怎麼判斷你的程式能不能用到某個物件，我們後面會提到。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;Garbage Collection 收集與釋放的對象&lt;/h2&gt;
&lt;p&gt;在 Java 的世界裡頭，GC 佔了很重要的角色，他會幫你管理和釋放 Heap 上你已經用不到物件所佔的空間，並把他配置給其他需要用到的物件。&lt;/p&gt;
&lt;p&gt;所以接下來這個問題就很重要了－－GC 所管理和回收的記憶體空間，到底是以什麼東西為單位？&lt;/p&gt;
&lt;p&gt;如果你去看 &lt;a class="reference external" href="http://java.sun.com/products/hotspot/docs/whitepaper/Java_HotSpot_WP_Final_4_30_01.html"&gt;The Java HotSpot Virtual Machine&lt;/a&gt; 和 &lt;a class="reference external" href="http://docs.oracle.com/javase/specs/jls/se7/html/index.html"&gt;The Java™ Language Specification&lt;/a&gt; 裡關於 GC 的說明的話，就會發現 Java 裡的 GC 作用的層級都是「物件」。&lt;/p&gt;
&lt;p&gt;也就是說，就 Java Programmer 的觀點來看，GC 所回收的記憶體是一個個用不到的「物件」所佔據的 Heap 空間，而不是零碎的記憶體區塊。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;到底什麼是 Java 物件？&lt;/h2&gt;
&lt;p&gt;在 Java 當中，對於「物件 / object」這個詞有相當嚴謹的定義，同樣的，在 The Java™ Language Specification 這份規格書中，4.3.1 中對於 object 的定義如下：&lt;/p&gt;
&lt;blockquote&gt;
An object is a class instance or an array.&lt;/blockquote&gt;
&lt;p&gt;我們已經知道 array 就是我們常用的陣列，那 &lt;tt class="docutils literal"&gt;class instance&lt;/tt&gt; 又是什麼呢？簡單的說，就是當你在程式碼中寫下 &lt;tt class="docutils literal"&gt;new MyClass()&lt;/tt&gt; 這樣的程式碼時，Java 在 Heap 上配置給你的那個東西，也就是我們所熟悉的物件。&lt;/p&gt;
&lt;p&gt;請注意這裡隱含了兩個意義：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Java 裡，你用那八個以小寫開頭的 &lt;strong&gt;primitive type&lt;/strong&gt; 宣告的變數&lt;strong&gt;不是物件&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;既然物件是存在 Heap 當中，我們一定要透過 Reference 才能存取到他&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="java-gc"&gt;
&lt;h2&gt;Java GC 的運作方式&lt;/h2&gt;
&lt;p&gt;現在我們已經知道 GC 的最主要的工作之一是想辦法找出 Heap 當中已經不能被程式使用到的物件，這樣才能知道哪些物件所佔的空間可以被釋放，但這要怎麼做呢？&lt;/p&gt;
&lt;p&gt;基本上目前絕大部份的 Java VM 用的方法，都是基於一個叫做 &lt;a class="reference external" href="http://www.brpreiss.com/books/opus5/html/page424.html"&gt;Mark and Sweep&lt;/a&gt; 的演算法概念，雖然為了 GC 的效率上而有一些變型，但基礎的概念是相同的。&lt;/p&gt;
&lt;p&gt;這個演算法的想法很簡單：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Heap 上的物件分成可以被程式「接觸」到，和不能被程式接觸到這兩種類別&lt;/li&gt;
&lt;li&gt;想辦法把所有可以被「接觸」到的物件打個勾，說這些是不能動的物件，是 Programmer 還有可能操作到的物件&lt;/li&gt;
&lt;li&gt;當步驟二結束後，那些沒有被標記到的物件就被視為垃圾，因為理論上我們不可能再透過任何方式拿到那個物件了。&lt;/li&gt;
&lt;li&gt;接著，GC 就可以安全地清除那些沒有被標記到的物件&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;順道一提，&lt;a class="reference external" href="http://www.artima.com/insidejvm/applets/HeapOfFish.html"&gt;這篇文章&lt;/a&gt;中有一個互動式的 Java Applet 可以玩，可以讓你模擬在 Heap 中生成物件 (Allocate Fish）、將 Reference 指到物件（Assign Reference）和 GC 的過程，有興趣的可以玩玩看。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="gc"&gt;
&lt;h2&gt;GC 如何判斷一個物件可不可以被接觸&lt;/h2&gt;
&lt;p&gt;所以現在要回答的一個問題，就是 GC 要如何判斷某個物件能不能被某個程式接觸到呢？&lt;/p&gt;
&lt;p&gt;回想一下我們到現在所知道的幾個事實：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;所有的 Java 物件都放在 Heap 上&lt;/li&gt;
&lt;li&gt;在 Java 裡我們是透過名叫 Reference 的 Pointer 去取得 Heap 中的物件來操作&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;沒錯！你答對了－－只要一個物件在 Java 程式裡，再也沒辦法讓寫程式的人尋著 Reference 去找到他，那他就被視作無法被接觸到，而會被當成垃圾。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="myobject-null"&gt;
&lt;h2&gt;&lt;tt class="docutils literal"&gt;myObject = null&lt;/tt&gt; 實際上的意義&lt;/h2&gt;
&lt;p&gt;再回過頭來討論，在 Java 當中我們要怎麼樣讓 GC 認為一個物件已經是「不可被接觸到」的狀態呢？&lt;/p&gt;
&lt;p&gt;你可能會在很多教你寫 Java 的書當中看到，他會告訴你物件不用的時候，需要把他設為 null 來釋放記憶體，但這句話到底是什麼意思呢？&lt;/p&gt;
&lt;p&gt;講了一堆理論，我們還是直接來看程式碼的行為會比較清楚：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;object2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;object1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 假設 GC 在這個時候被執行&lt;/span&gt;
        &lt;span class="c1"&gt;// System.out.println(object1.x); // 會丟出 NullPointerException&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;我們現在應該很清楚這隻程式到 object1.x 為止的記憶體狀態了，所以這邊的重點在於 &lt;tt class="docutils literal"&gt;object1 = null&lt;/tt&gt; 這行到底會發生什麼事。&lt;/p&gt;
&lt;p&gt;你現在應該已經知道，object1 這個變數實際上是存在 Stack 上面，而我們用 = 號來指定東西給 object1 時，實際上是改變 Stack 上 object1 這個變數的內容了。&lt;/p&gt;
&lt;p&gt;所以如果我們把記憶體狀態畫出來，就會變成像下面這個樣子：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/01.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/01.png" src="http://bone.twbbs.org.tw/Attachments/ch4/01.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/02.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/02.png" src="http://bone.twbbs.org.tw/Attachments/ch4/02.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/03.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/03.png" src="http://bone.twbbs.org.tw/Attachments/ch4/03.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/04.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/04.png" src="http://bone.twbbs.org.tw/Attachments/ch4/04.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/05.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/05.png" src="http://bone.twbbs.org.tw/Attachments/ch4/05.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;現在你可以看到，實際上 null 存放的地方是 Stack 上的變數，而當某個 reference 變數他存的地址被設成 null 時，其語意是「這個 reference 不再指到任何東西」，他存的是一個特殊的數字，代表不指到任何記憶體地址。&lt;/p&gt;
&lt;p&gt;而現在看到上面的圖，你也應該很清楚怎麼樣判斷一個物件是不是「無法被接觸」了－－只要你沒辦法從你的 Call stack 當中的箭頭找到他時，這個物件就可以被當成是「無法被接觸」了。&lt;/p&gt;
&lt;p&gt;換句話說，當我們經過了 &lt;tt class="docutils literal"&gt;object1 = null&lt;/tt&gt; 這一行時，原本 object1 所指到 object 實際上在我們的程式裡就不可能被用到了，因為所有在 Call Stack 上的變數都無法讓我們找他，而這時 GC 就會把這個物件當成是可以回收的狀態。&lt;/p&gt;
&lt;p&gt;同時現在你也應該知道，為什麼如果在最後一行加上 &lt;tt class="docutils literal"&gt;System.out.println(object1.x);&lt;/tt&gt; 的話，Java 會丟出 &lt;tt class="docutils literal"&gt;java.lang.NullPointerException&lt;/tt&gt; 這個錯誤了。因為 null 代表的是不指到任何記憶體地址，你自然不能用 . 號去取得那個記憶體地址的內容了。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="reference"&gt;
&lt;h2&gt;如果物件裡還有其他的 Reference 呢？&lt;/h2&gt;
&lt;p&gt;上面我們看的是很簡單的例子，類別裡面的成員變數就只有基本資料型態而已。那如果我們的成員變數裡面又有其他的 Reference，而且還指到了其他的物件，那記憶體狀態會是什麼，什麼樣的物件又會被視為可回收呢？&lt;/p&gt;
&lt;p&gt;還是一樣，一段 Code 和幾張圖抵過千言萬語，直接來看程式碼唄：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AnotherObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="n"&gt;AnotherObject&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AnotherObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;object2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;object2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在上一篇文章中，我們已經知道了當 &lt;tt class="docutils literal"&gt;object1.x = 20&lt;/tt&gt; 這行程式碼執行完之後，記憶體當裡面的架構會長得像下面一樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/06.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/06.png" src="http://bone.twbbs.org.tw/Attachments/ch4/06.png" style="width: 600px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接下來我們用 &lt;tt class="docutils literal"&gt;object1.z = null&lt;/tt&gt; 把 object1 裡的 z 這個變數的內容設成 null，所以現在記憶體狀態會變成像下面一樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/07.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/07.png" src="http://bone.twbbs.org.tw/Attachments/ch4/07.png" style="width: 600px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;現在你會發現，我們的 Heap 上有一個 AnotherObject 再也無法從 Call Stack 上的任何箭頭找到，所以可以被 GC 視為是垃圾。&lt;/p&gt;
&lt;p&gt;另外你也應該發現，object1 所指到的物件裡的 z 變數，他所佔的那塊空間，仍然好端端地活在那個 SimpleObject 物件中，沒有被標上藍色，這是很重要的一個觀念－－&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;當某個的 Java 物被 GC 視為可回收時，若原本指到他的那個 reference 是某個物件的成變數，那其所需要的「存地址」的空間都還會被繼續佔用，只是他存放的已經不是原本的記憶體地址，可能指到其他的新物件，也可能是 null。&lt;/li&gt;
&lt;li&gt;一個物件的成員變數所佔的空間，只有在該物件本身被視為可回收之後，才有可能被釋放。&lt;/li&gt;
&lt;li&gt;換句話說，Java 物件的成員變數的生命週期和物件的生命週期是一致的。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;最後，我們來看看將 object2 設成 object1 之後會發生什麼事情。&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;object2 = object1&lt;/tt&gt; 由於兩邊都是「地址」，所以這個動作實際上是把 Stack 上 object1 所存的記憶體位置，複製一份到現在 Stack 上 object2 變數所在的地方，而就語意上來看，就是現在 object1 和 object2 都指到同一個物件。&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/08.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/08.png" src="http://bone.twbbs.org.tw/Attachments/ch4/08.png" style="width: 600px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;由於 object2 已經被設為原來 object1 所指到的物件，現在 Call Stack 上沒有任何 Reference 指到 Heap 上方的那個 SimpleObject，所以他被標成藍色是理所當然的。&lt;/p&gt;
&lt;p&gt;接著問題就來了，被標成藍色的那個 SimpleObject 裡的 z 所指到的那個 AnotherObject 到底該不該被視為可以被回收的垃圾呢？&lt;/p&gt;
&lt;p&gt;請回想我們對於垃圾的判斷－－無法從 Call Stack 上的箭頭尋線找到的物件。&lt;/p&gt;
&lt;p&gt;現在你應該很清楚了，因為我們無法從 Call Stack 上找到藍色的那個 SimpleObject 物件，自然也就無法透過那個 SimpleObject 物件的 z 找到 AnotherObject，所以 AnotherObject 可以被標上藍色。&lt;/p&gt;
&lt;p&gt;所以我們最終的記憶體狀態會像下面一樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/09.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/09.png" src="http://bone.twbbs.org.tw/Attachments/ch4/09.png" style="width: 600px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;而如果 GC 在這個時間點執行，並且決定要清掉所有垃圾的話，那些被標成藍色的物件就會被清掉，變成下像面一樣，而原本被藍色物件佔據的空間就可以再配置給其他需要的物件：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/10.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/10.png" src="http://bone.twbbs.org.tw/Attachments/ch4/10.png" style="width: 600px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;這邊還是要再重提一次，雖然 object1.z 所指到的物件已經被回收消失了，但 object1.z 那個存 reference 的空間還是存在那個活著的 SimpleObject 當中的－－&lt;strong&gt;Java 的 GC 回收的是「物件」，而不是單獨的「成員變數」，這一點是想要理解 Java 的記憶體管理機制時很重要的一點&lt;/strong&gt;。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id8"&gt;
&lt;h2&gt;在方法中製造物件&lt;/h2&gt;
&lt;p&gt;還記得我們在第二篇裡提到的那個 C 語言的記憶體洩漏的例子嗎？&lt;/p&gt;
&lt;p&gt;如果這次我們把那隻程式改成如下的 Java 程式碼，在程式碼裡 malloc 改成 new 一個物件，但是在函式結束前沒有把指到物件的 reference 變數設成 null，這時又會發生什麼事情呢？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;requireHeap&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;requireHeap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;綜合之前所談的 Call Stack 和區域變數的關係，以及 Heap 和 Java 物件還有 Reference 的互動，現在你應該很容易可以畫出來，在程式執行到 &lt;tt class="docutils literal"&gt;System.out.println(x);&lt;/tt&gt; 時的記憶體狀態了：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/11.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/11.png" src="http://bone.twbbs.org.tw/Attachments/ch4/11.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;但接下來問是就來了，當 requireHeap() 返回之後會發生什麼事呢？&lt;/p&gt;
&lt;p&gt;還記得放在 Stack 上的區域變數的存活時間只到函數返回前嗎？所以當我們的 requireHeap() 結束，返回 main() 後，放在 Stack 上的 requireHeap() 裡的區域變數就消失了，圖會變像下面這樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/12.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/12.png" src="http://bone.twbbs.org.tw/Attachments/ch4/12.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;你會發現，現在 Call Stack 上已經沒有任何箭頭可以讓你指到原本的 SimpleObject 了，而在這樣的情況下，這個 SimpleObject 當然可以被標成藍色，視為是可以回收的物件了。&lt;/p&gt;
&lt;p&gt;換句話說，就算你呼叫了一百次的 requireHeap()，也只會在 Heap 上產生一百個可以被視為垃圾的物件，而 Java 的 GC 會在需要記憶體時去釋放你那些垃圾物件所佔的空間。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="java-memory-leak"&gt;
&lt;h2&gt;所以 Java 不會有 Memory Leak 嗎？&lt;/h2&gt;
&lt;p&gt;不，雖然 Java 有很好用的 GC 來讓寫程式的人不用太花腦力自行去釋放記憶體，但當你的程式複雜到一定程度的時候，很有可能你不知不覺地在無意間一直持有著某個實際上已經不會再被使用的物件的 reference。&lt;/p&gt;
&lt;p&gt;關於這個問題，如果你有興趣，你可以看 &lt;a class="reference external" href="http://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java"&gt;StackOverflow 上的這個討論&lt;/a&gt;，裡面有一些有趣的例子，但你也應該會注意到，要在 Java 中刻意製造 Memory Leak 好像不像 C 語言這麼簡單。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id9"&gt;
&lt;h2&gt;為什麼整數的成員變數不可能被 GC 單獨回收&lt;/h2&gt;
&lt;p&gt;到這邊，我們已經把 Java 的變數分類、物件的定義，以及 Java 物件在 Heap 裡的生成和如何被 GC 給回收介紹過一次了。&lt;/p&gt;
&lt;p&gt;現在你應該可以知道為什麼我會在上一篇&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-14-androidlifecycle.rst.html"&gt;講 Android 的文章&lt;/a&gt;中提到，雖然我不清楚那個整數變數忽然不預期地被歸零的真正原因，但卻認為不太可能是 GC 把他回收掉所造成的理由了。&lt;/p&gt;
&lt;p&gt;因為在 Java 裡，一個成員變數的生命週期和物件是一致的，只有在該物件被 GC 消滅之後，他所佔的空間才會被釋放出來。&lt;/p&gt;
&lt;p&gt;再從另外一個角度來看，在 Java 裡面，GC 的作用單位是一個 Java 物件，但在 Java 裡整數是 primitive type 而不是物件，一個根本不是物件的 primitive type 的變數所佔據的空間，會直接被 GC 幹走，也是不可思議的。&lt;/p&gt;
&lt;p&gt;所以「一個屬於 primitive type 的成員變數被 GC 回收」這件事，本身就與 Java 對於記憶體的操作模型相違背。&lt;/p&gt;
&lt;p&gt;除非你用的 Java 環境的 GC 有 bug，又或者是那個 GC 完全無視 Java 對於物件和變數的生命週期的定義，故意在 Heap 上的某個物件裡挖一個洞給你跳，不然正常來說，型態為整數的成員變數，其所佔用的空間不可能在所屬物件被消滅之前被回收，而這兩種情況，都不會是 Java 的 GC 的正常行為。&lt;/p&gt;
&lt;p&gt;這點在 Android 上也是一樣的，因為雖然 Android 用的是 Dalvik VM，但他的 GC 對於物件的回收機制和一般的 Java VM 並沒有什麼不同，有興趣的話可以參考這個 &lt;a class="reference external" href="http://dubroy.com/blog/google-io-memory-management-for-android-apps/"&gt;Google IO 的演講&lt;/a&gt;。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id10"&gt;
&lt;h2&gt;整數真的不會被回收嗎？&lt;/h2&gt;
&lt;p&gt;是的，只要你的 Java VM 所用的 GC 沒有 bug，也沒有亂挖洞給你跳的話。&lt;/p&gt;
&lt;p&gt;在正常的情況下，你宣告為物件成員變數的整數變數所佔的空間，在那個物件被回收之前，是不會被 GC 動到的，除非－－你以為他是個整數，但實際上他根本是個 Java 物件。&lt;/p&gt;
&lt;p&gt;這是因為 Java 有一個叫 Auto Boxing / Unboxing 的機制，會讓你很方便的把 primitive type 的變數變成 Integer / Long / Double... 等的物件，但這個時候，常常都會有一些陷阱在。&lt;/p&gt;
&lt;div class="section" id="auto-boxing"&gt;
&lt;h3&gt;Auto-boxing 範例&lt;/h3&gt;
&lt;p&gt;至於什麼是 Auto-boxing 呢？下面的程式碼可以表現出這件事情：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrapObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TrapObject&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;TrapObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;object.x:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;object.y:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hashCode:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;identityHashCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;object.x:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;object.y:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hashCode:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;identityHashCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;object.x:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;object.y:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hashCode:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;identityHashCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;現在的你，看到這樣的程式碼，應該可以說出以下的幾件事情：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;main() 函式裡的 object 變數是放在 Stack 上，而且是個 Reference 變數，存的是記憶體地址&lt;/li&gt;
&lt;li&gt;TrapObject 裡的 x 是 primitve type，也就是他存的是「數值」。&lt;/li&gt;
&lt;li&gt;TrapObject 裡的 y 是大寫開頭的 Integer，而且還可以被指為 null，所以他一定是 Reference，存的是地址。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;接著我們同樣一步一步地把程式執行的時候的情況畫出來，一開始是用 &lt;tt class="docutils literal"&gt;new TrapObject()&lt;/tt&gt; 建立物件：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/12.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/13.png" src="http://bone.twbbs.org.tw/Attachments/ch4/13.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;接著會執行 &lt;tt class="docutils literal"&gt;object.y = object.x;&lt;/tt&gt; 這行……咦？！&lt;/p&gt;
&lt;p&gt;不是說 object.y 是 Reference 而 object.x 是整數嗎？為什麼整數可以被指派給一個 Reference 呢？&lt;/p&gt;
&lt;p&gt;答案是……Java 實際上會幫你把 &lt;tt class="docutils literal"&gt;object.y = object.x&lt;/tt&gt; 這句話，翻譯成 &lt;tt class="docutils literal"&gt;object.y = new Integer(object.x)&lt;/tt&gt;！&lt;/p&gt;
&lt;p&gt;看到 &lt;tt class="docutils literal"&gt;new&lt;/tt&gt; 這個關鍵字了嗎？沒錯，我們實際上是在 Heap 上多產生出一個 Integer 物件，並且再把這個 Integer 物件的記憶體地址指派給 &lt;tt class="docutils literal"&gt;object.y&lt;/tt&gt; 這個 Reference，所現在實際上的記憶體狀態是這樣的：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/14.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/14.png" src="http://bone.twbbs.org.tw/Attachments/ch4/14.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;所以在這個時間點把 object.x 和 object.y 印出來的話，就會像下面一樣，其中 hashCode 是 Java 從 Integer 物件所在的記憶體地址所算出來的值。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="go"&gt;object.x:10&lt;/span&gt;
&lt;span class="go"&gt;object.y:10&lt;/span&gt;
&lt;span class="go"&gt;hashCode:1560511937&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;接著，我們再把 object.x 設成 20，這個時候你應該已經可以判斷出 object.x 是 primitive type，所以我們改的是 TrapObject 裡 x 的值，變成下面這樣子：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/15.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/15.png" src="http://bone.twbbs.org.tw/Attachments/ch4/15.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;而因為那個 Integer 物件，完全沒被我們動到，所以再次輸出的結果，會是 object.x 被更動了，但 object.y 還是維持原樣：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="go"&gt;object.x:20&lt;/span&gt;
&lt;span class="go"&gt;object.y:10&lt;/span&gt;
&lt;span class="go"&gt;hashCode:1560511937&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;最後，我們再來看看 &lt;tt class="docutils literal"&gt;object.y = 30&lt;/tt&gt; 這行會做什麼事情……&lt;/p&gt;
&lt;p&gt;沒錯，你答對了！Java 又幫我們在 Heap 上多生出了一個 Integer 物件，並且把 object.y 這個 reference 指到的地址改成新的 Integer 物件，變成像下面一樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch4/16.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch4/16.png" src="http://bone.twbbs.org.tw/Attachments/ch4/16.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;所以現在再把他印出來的話，會發現 object.y 的值不但更改了，甚至連從記憶體地址算出來的 object.y 的 hashCode 都改了，就是因為現在的 object.y 已經指到了一個全新的物件的原因。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="go"&gt;object.x:20&lt;/span&gt;
&lt;span class="go"&gt;object.y:30&lt;/span&gt;
&lt;span class="go"&gt;hashCode:306344348&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在上面的例子，我們就可以看到一個「整數被 GC 回收」的假象，但實際上被 GC 回收的卻是「一個存放整數的盒子 （Integer 物件）」這樣的物件。&lt;/p&gt;
&lt;p&gt;而且只要你沒把 object.y 設成 null 的話，那目前 object.y 所指到的整數在 TrapObject 被標成垃圾前，也不可能被回收，因為 GC 在標記要清除物件的時候，一定可以從 TrapObject 裡的 y 找到他，並幫他打勾。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id11"&gt;
&lt;h3&gt;其他 Auto-boxing 的場合&lt;/h3&gt;
&lt;p&gt;在 Java 中最容易遇到 Auto-Boxing 的地方，應該就是你在用各類的 Collection 時了！&lt;/p&gt;
&lt;p&gt;例如你可能很習慣當要一個不定長度的陣列時來存放整數時，用 ArrayList 來做，寫成像下面的程式碼一樣：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.ArrayList&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrapObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;這個時候你應該會發現，ArrayList 裡放的是 Integer！而 Integer 是實實在在的 Reference Type，所以實際上這段程式碼會像下面一樣，在 Heap 上產生三個 Integer 物件。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.ArrayList&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrapObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id12"&gt;
&lt;h2&gt;小結&lt;/h2&gt;
&lt;p&gt;這次我們花了相當的篇幅，來說明 Java 中如何解決釋放 Heap 上所使用到的空間，GC 的作用單位，以及 Java 的類別裡面，物件成員變數的生命週期，還有資料型態對於 GC 的影響……等等。&lt;/p&gt;
&lt;p&gt;到這邊，我們終於把 Stack / Heap 上的變數稍微帶過一遍了，但你應該也發現目前我們完全沒有提到當 C 語言的函式或 Java 的方法帶有參數時，會發生什麼事情。&lt;/p&gt;
&lt;p&gt;所以下一次，我們就會來看看 C 語言函式裡面的參數到底在搞什麼鬼。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id13"&gt;
&lt;h2&gt;參考資料&lt;/h2&gt;
&lt;p&gt;如果你想要知道 Java VM （特別是 Sun / Oracle官方的 HotSpot VM）實際上如何管理記憶體，可以參照以下兩份文件：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://java.sun.com/products/hotspot/docs/whitepaper/Java_HotSpot_WP_Final_4_30_01.html"&gt;The Java HotSpot Virtual Machine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf"&gt;Memory Management in the Java HotSpot Virtual Machine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">墳墓 (Brian Hsu)</dc:creator><pubDate>Wed, 18 Apr 2012 19:24:00 +0800</pubDate><guid>http://bone.twbbs.org.tw/blog/programming_2012-04-18-candjavamemory4.rst.html</guid><category>C</category><category>Java</category><category>記憶體管理</category></item><item><title>淺談 C 語言和 Java 的記憶體管理（三）</title><link>http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory3.rst.html</link><description>&lt;div class="section" id="id1"&gt;
&lt;h2&gt;目錄&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-16-candjavamemory.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（一）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory2.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（二）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;淺談 C 語言和 Java 的記憶體管理（三）&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-18-candjavamemory4.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（四）&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="java-stack-heap-pointer"&gt;
&lt;h2&gt;Java 也有 Stack / Heap / Pointer 這些東西嗎？&lt;/h2&gt;
&lt;p&gt;前兩篇文章，我們提到了在 C 語言裡 Stack 與區域變數之間的關係，還有如何用 Pointer 來操作向程式語言 runtime 要回來的 Heap 空間。&lt;/p&gt;
&lt;p&gt;你可能會覺得疑惑，這些東西在 Java 當中也有嗎？特別是 Pointer 這東西，大家不是都說 Java 因為沒有 Pointer 所以比 C++ 簡單好學嗎？&lt;/p&gt;
&lt;p&gt;事實上，這些東西在 Java 中通通都有，只是 Java 把他隱藏得很好，讓你覺得這些東西好像不存在一樣，但如果你沒有這些觀念，卻會讓你覺得 Java 好像捉摸不定。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="java"&gt;
&lt;h2&gt;Java 變數的分類&lt;/h2&gt;
&lt;p&gt;如果你是 Java Programmer，應該相當習慣了當你在宣告某些變數的時候，他的型態開頭是小寫：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;byte&lt;/li&gt;
&lt;li&gt;short&lt;/li&gt;
&lt;li&gt;int&lt;/li&gt;
&lt;li&gt;long&lt;/li&gt;
&lt;li&gt;char&lt;/li&gt;
&lt;li&gt;float&lt;/li&gt;
&lt;li&gt;double&lt;/li&gt;
&lt;li&gt;boolean&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你也應該習慣了，除了上述八種資料型態外，其他的資料型態應該要用大寫開頭，如果你看到一個變數的宣告是 &lt;tt class="docutils literal"&gt;myType x&lt;/tt&gt; 這樣，應該立馬會覺得寫出這行程式碼的傢伙對 Java 一定不熟。&lt;/p&gt;
&lt;p&gt;這是因為在 Java 裡變數分成兩大類，一種是 &lt;tt class="docutils literal"&gt;Primitive Type&lt;/tt&gt;，也就是上述列出來的八個小寫開頭的資料型態，而其他所有的資料型態，不論他是啥 Java 類別或介面，一律都算做 &lt;tt class="docutils literal"&gt;Reference&lt;/tt&gt; 資料型態，而在 Java 的慣例裡，Reference 型態的資料型別習慣上都是大寫開頭。&lt;/p&gt;
&lt;p&gt;那 Primitive Type 和 Reference 有什麼不同呢？&lt;/p&gt;
&lt;p&gt;簡單的來說如下：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Primitive Type 的變數裡存的是「值」，例如一個 &lt;tt class="docutils literal"&gt;int x = 10&lt;/tt&gt;，x 裡存的就是整數 10；一個 &lt;tt class="docutils literal"&gt;double y = 0.5&lt;/tt&gt;，y 那塊記憶體裡存的就是 0.5。&lt;/li&gt;
&lt;li&gt;Reference 存的是某樣東西的&lt;strong&gt;記憶體地址&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;看到關鍵字了嗎？還記得有什麼東西存的也是記憶體地址嗎？答對了！就是 Pointer！&lt;/p&gt;
&lt;p&gt;所以你現在知道為什麼大家說 Java 裡沒有 Pointer 這句話有問題了吧？因為 Java 裡確實有 Pointer，只是他的 Pointer 叫做 Reference，而且你還天天在用他……&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;最簡單的類別的宣告與物件配置&lt;/h2&gt;
&lt;p&gt;我們已經知道，在 Java 裡，你宣告在方法當中的變數，都是存在 Stack 上了，所以 Stack 在 Java 當中確實是有被用到沒有錯，但 Heap 呢？我從來沒在 Java 裡向 JVM 要 Heap 裡的空間的樣子啊？&lt;/p&gt;
&lt;p&gt;沒錯，因為 Java 並不像 C 一樣允許你直接操作記憶體，但就某方面來說，你寫 Java 的時候用到 Heap 的機會比 C 還大很多－－因為所有你 new 出來的 Java 物件全部都長在 Heap 上。&lt;/p&gt;
&lt;p&gt;多說無益，我們直接來看程式碼吧：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;object2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;這個程式非常簡單，我們定義了一個 SimpleObject 的類別，並且在 main() 裡建立了兩個 SimpleObject 物件，然後把 object1 物件的整數變數 x 設成 20。&lt;/p&gt;
&lt;p&gt;這個時候的記憶體裡究竟發生了哪些事呢？這個時候你要試著回答：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;object1 和 object2 的變數放在記憶體的哪裡？&lt;/li&gt;
&lt;li&gt;我們 new 出來的物件又放在記憶體的哪裡？&lt;/li&gt;
&lt;li&gt;這兩個物件中的 x 變數又是在哪裡呢？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;答案如下：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;object1 和 object2 都放在 Stack 上給 main() 函式的空間&lt;/li&gt;
&lt;li&gt;這兩個物件都在 Heap 裡&lt;/li&gt;
&lt;li&gt;因為 x 是成員變數，所以是跟著物件，既然物件在 Heap 裡，他們也應該會在 Heap 裡。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;另外你也應該注意到了，你在寫 Java 的時候只有對於 Reference 型態的變數才會加上 . 後面接著某些東西，這個 . 就類似 C 的 Pointer 前加上 * 號做 deference 來取得 Pointer 指到的位置一樣。&lt;/p&gt;
&lt;p&gt;換句話說，你也可以把 object1.x 記成像「找出 object1 裡存的地址所指到的 Heap 空間當中的 x」這樣的意思。&lt;/p&gt;
&lt;p&gt;有了上述的概念之後，我們就可以試著把這個程式在執行時的記憶體狀態一步一步畫出來……&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch3/01.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch3/01.png" src="http://bone.twbbs.org.tw/Attachments/ch3/01.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch3/02.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch3/02.png" src="http://bone.twbbs.org.tw/Attachments/ch3/02.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch3/03.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch3/03.png" src="http://bone.twbbs.org.tw/Attachments/ch3/03.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch3/04.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch3/04.png" src="http://bone.twbbs.org.tw/Attachments/ch3/04.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;現在你應該很清楚自己在 Java 裡 new 一個物件出來的時候，會發什生麼事情了吧？&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;那再多一點成員變數呢？&lt;/h2&gt;
&lt;p&gt;你可能會問，在上面的例子裡 SimpleObject 只有一個 x 變數，如果我再加一個 y 變數上去，會變怎麼樣呢？其實也沒怎樣－－就是在 Heap 上的物件大了一點，多給你一點空間放 y 變數而已。&lt;/p&gt;
&lt;p&gt;例如下面的程式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;object2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在執行完 &lt;tt class="docutils literal"&gt;object1.x = 20&lt;/tt&gt; 這行之後，記憶體的狀況會如下圖所示：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch3/05.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch3/05.png" src="http://bone.twbbs.org.tw/Attachments/ch3/05.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="reference"&gt;
&lt;h2&gt;那如果成員變數裡也有 Reference 型態呢？&lt;/h2&gt;
&lt;p&gt;上面的兩個程式碼裡，我們的 SimpleObject 類別的成員變數都只有 int 和 double 這些以小寫開頭的基本資料型態，那如果我們的類別比較複雜，不只有這些數值的資料，還有其他物件呢？&lt;/p&gt;
&lt;p&gt;以下面的程式碼來看，我們的記憶體到底會變得怎麼樣呢？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AnotherObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleObject&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="n"&gt;AnotherObject&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AnotherObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;SimpleObject&lt;/span&gt; &lt;span class="n"&gt;object2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;object2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SimpleObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;只要你記得以下兩點：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;所有 Java 物件都在 Heap 上&lt;/li&gt;
&lt;li&gt;所有非基本資料型態的變數，都是 Reference，存的都是地址&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;那你應該很容易得出以下的結論：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;現在 Heap 上有四個物件：&lt;ol class="arabic"&gt;
&lt;li&gt;object1 指到的 SimpleObject&lt;/li&gt;
&lt;li&gt;object2 指到的 SimpleObject&lt;/li&gt;
&lt;li&gt;object1 指到的 SimpleObject 裡的 z 指到的 AnotherObject&lt;/li&gt;
&lt;li&gt;object2 指到的 SimpleObject 裡的 z 指到的 AnotherObject&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;既然 object1 和 object2 裡面的 z 都是 Reference，他們存的一定是記憶體地址！&lt;/li&gt;
&lt;li&gt;所以 object1.z 存的是上述的第 3 個物件&lt;/li&gt;
&lt;li&gt;而 object2.z 存的是上述的第四個物件&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;有了這些線索，我們就可以很簡單地把記憶體當中的情況畫出來了：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch3/06.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch3/06.png" src="http://bone.twbbs.org.tw/Attachments/ch3/06.png" style="width: 600px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h2&gt;小結&lt;/h2&gt;
&lt;p&gt;這一次我們講的是關於 Java 中物件和 Heap 的關係，你可以看到 Java 是如何存放我們所 new 出來的物件，和他與區域變數之間的關係。&lt;/p&gt;
&lt;p&gt;這一節的重點如下：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Java 的資料型態分成 primitive type 和 reference type 兩種，其中 reference type 其實就是指標，存的是記憶體地址。&lt;/li&gt;
&lt;li&gt;Java 裡所有的物件都是存在 Heap 上面。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;到了這邊，你應該可以了解其實當你 new 一個 Java 物件的時候，其實就和 C 語言的 malloc 一樣，是在向程式語言的 runtime（這裡的話就是 JVM）要 Heap 的空間。&lt;/p&gt;
&lt;p&gt;可是你可能也注意到了，你在寫 Java 程式的時候，從來沒有寫過「把記憶體還給 JVM」這樣的程式碼，頂多是把某個 Reference 變數設為 null 而已，下次我們會詳細來看為什麼在寫 Java 時，不需要明確地把從 Heap 挖來的空間歸還給 JVM。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id8"&gt;
&lt;h2&gt;目錄&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-16-candjavamemory.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（一）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory2.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（二）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;淺談 C 語言和 Java 的記憶體管理（三）&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-18-candjavamemory4.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（四）&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">墳墓 (Brian Hsu)</dc:creator><pubDate>Tue, 17 Apr 2012 21:44:00 +0800</pubDate><guid>http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory3.rst.html</guid><category>C</category><category>Java</category><category>記憶體管理</category></item><item><title>淺談 C 語言和 Java 的記憶體管理（二）</title><link>http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory2.rst.html</link><description>&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;這篇文章中的例子講的都是 C 語言，但如果你是寫 Java 的，也誠心的建議你可以耐心地閱讀，就算程式碼看不懂也沒關係。最主要的是要知道這篇文章中講的關於 Heap 的配置、有一種變數存的是記憶體位置……等等的概念，這些事情對你了解 Java 的物件是如何存在記憶體當中相當有幫助。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;目錄&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-16-candjavamemory.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（一）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;淺談 C 語言和 Java 的記憶體管理（二）&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory3.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（三）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-18-candjavamemory4.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（四）&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="heap"&gt;
&lt;h2&gt;Heap 是什麼？&lt;/h2&gt;
&lt;p&gt;上一篇文章中，我們談到了記憶體分為兩種，以及 Stack 與區域變數之間的關係，這一篇文章，我們就來看看到底 Heap 是什麼東西。&lt;/p&gt;
&lt;p&gt;現在你應該已經知道了，在 C / Java 裡，宣告在 C 的函式或 Java 的方法裡的區域變數，全都是放在 Stack 上，而這些變數在函式執行結束之後，就會被消滅，你不能也不應該去存取他。&lt;/p&gt;
&lt;p&gt;但有的時候，我們確實需要讓某些資料在函式結束之後也存在，甚至可以被其他的函式所存取，那應該要怎麼辦呢？&lt;/p&gt;
&lt;p&gt;這個時候，就是 Heap 的回合了！&lt;/p&gt;
&lt;p&gt;你可以把 Heap 想像成是一個由程式語言 runtime 管理的空間銀行，當你需要一些空間來存放資料的時候，你就和程式語言 runtime 講說：「喂，我要 30MB 的記憶體空間。」&lt;/p&gt;
&lt;p&gt;接著程式語言 runtime 就會把 30MB 的記憶體空間騰出來，並且把這塊記憶體空間的開頭位址給你，讓你知道你所要到的記憶體到底是在哪。而等你不需要用的時候，再把這塊 30MB 的記憶體還給程式語言 runtime ，讓其他人可以使用這塊記憶體。&lt;/p&gt;
&lt;p&gt;在這部份，事實上程式語言 runtime 有一套非常複雜的管理機制，來盡量確保可以在記憶體當中挖出 30MB 來給你，但現在不需要擔心那麼多，只要知道程式語言 runtime 會幫你處理這些事，而他真的挖不出來的時候也會告訴你記憶體爆炸了就好。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="c-heap"&gt;
&lt;h2&gt;C 語言如何在 Heap 挖資料&lt;/h2&gt;
&lt;p&gt;就像我們在上一篇所講的，C 語言所有的區域變數都是存在 Stack 上，那我們要怎麼樣操作從 Heap 挖出來的記憶體呢？&lt;/p&gt;
&lt;p&gt;別緊張，還記得剛剛說過，當我們像程式語言 runtime 要記憶體的時候，程式語言 runtime 會給我們那塊記憶體的開頭位址嗎？
我們只要把那塊記憶體的開頭位置存到區域變數當中，當要用到 Heap 上的資料時，再從那個變數裡存的記憶體位置去拿就好。&lt;/p&gt;
&lt;p&gt;這種&lt;strong&gt;存放記憶體位置的變數，在 C 語言裡就稱為「指標」，英文叫 Pointer&lt;/strong&gt;。所以以後寫 C 語言遇到 Pointer 請不要再害怕了，他和一般的變數沒有什麼不同，就只是存的是記憶體位址而已。&lt;/p&gt;
&lt;p&gt;至於要怎麼樣向程式語言 runtime 要 Heap 中的記憶體呢？&lt;/p&gt;
&lt;p&gt;在 C 語言的標準函式庫裡有各種不同的函式可以用，但你最常見到的應該會是 &lt;tt class="docutils literal"&gt;void * malloc(size_t size)&lt;/tt&gt; 這一個，你傳你所需要的大小進去，C 語言就會幫你向程式語言 runtime 要 Heap 中的記憶體，然後把指到該塊記憶體開頭的 pointer 給你。&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;在 C 語言裡，void * 代表「指到不一定是什麼資料型態的資料的指標」&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;廢話不多說，我們直接來看 C 語言的程式要怎麼寫：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include &amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;requireHeap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;

    &lt;span class="n"&gt;heapInt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;heapInt: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;*heapInt: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heapInt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;*heapInt: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;amp;(*heapInt): %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;requireHeap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;上面的程式碼進行的事情很簡單，我們在 requireHeap 裡用 &lt;tt class="docutils literal"&gt;malloc&lt;/tt&gt; 向程式語言 runtime 要了一塊大小為 &lt;tt class="docutils literal"&gt;sizeof(int)&lt;/tt&gt; 的記憶體，也就是一塊大小可以存放一個整數的記憶體空間，這個時候 malloc 會回傳一個 pointer 給我們，因為我們已經知道我們要存的是整數，所以我們用 &lt;tt class="docutils literal"&gt;(int *)&lt;/tt&gt; 將回傳來的 pointer 強制轉型成為指到整數資料的 pointer。&lt;/p&gt;
&lt;p&gt;換話句話，在 C 語言裡面，你需要指到什麼樣的資料型態的 pointer，就在該型態後面加個 * 號就好，例如 &lt;tt class="docutils literal"&gt;int *&lt;/tt&gt; 是指到 int 類型的資料，&lt;tt class="docutils literal"&gt;double *&lt;/tt&gt; 是指到 double 類型的資料，依此類推……&lt;/p&gt;
&lt;p&gt;這個時候要記得 heapInt 存的是記憶體位置，所以如果你執行這個程式的話，會看到如下的輸出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="go"&gt;brianhsu@USBGentoo ~ $ ./a.out&lt;/span&gt;
&lt;span class="go"&gt;heapInt: 25042960&lt;/span&gt;
&lt;span class="go"&gt;*heapInt: 0&lt;/span&gt;
&lt;span class="go"&gt;*heapInt: 10&lt;/span&gt;
&lt;span class="go"&gt;&amp;amp;(*heapInt): 25042960&lt;/span&gt;

&lt;span class="go"&gt;brianhsu@USBGentoo ~ $ ./a.out&lt;/span&gt;
&lt;span class="go"&gt;heapInt: 8540176&lt;/span&gt;
&lt;span class="go"&gt;*heapInt: 0&lt;/span&gt;
&lt;span class="go"&gt;*heapInt: 10&lt;/span&gt;
&lt;span class="go"&gt;&amp;amp;(*heapInt): 8540176&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;你會看到每次執行時，heapInt 都會出現奇怪的數字，而且這個數字每次都不太一樣。&lt;/p&gt;
&lt;p&gt;這是因為 heapInt 存的是&lt;strong&gt;記憶體位置&lt;/strong&gt;，而你每次向程式語言 runtime 要記憶體的時候，要到的地方都會不太一樣！&lt;/p&gt;
&lt;p&gt;接著問題就來了，既然 heapInt 裡存的只是記憶體的位置，那我們要怎麼樣才能存東西到那塊記憶體裡面去呢？在 C 語言之中，我們可以在一個型態為 pointer 的變數前加上 * 號來達成這件事，通常我們叫這個動作為「取值」或是 dereference。&lt;/p&gt;
&lt;p&gt;所以我們可以在 printf 用 &lt;tt class="docutils literal"&gt;*heapInt&lt;/tt&gt; 來印出這塊 Heap 記憶體中的內容，這個時候是 0。&lt;/p&gt;
&lt;p&gt;緊接著再用 &lt;tt class="docutils literal"&gt;*heapInt = 10&lt;/tt&gt; 來將 10 這個值塞進這塊 Heap 當中，這個時候再印一次 &lt;tt class="docutils literal"&gt;*heapInt&lt;/tt&gt; 的話，就會是 10 了。&lt;/p&gt;
&lt;p&gt;透過這樣子用 pointer 進行 dereference 的動作，我們就可以對向程式語言 runtime 要來的 Heap 空間做讀取和寫入的動作。&lt;/p&gt;
&lt;p&gt;最後，由於當你寫 &lt;tt class="docutils literal"&gt;(*heapInt)&lt;/tt&gt; 的時候，指的就是程式語言 runtime 配置的那塊記憶體，所以如果你再用 C 裡「取記憶體地址」的 &amp;amp; 運算元的話，就會發現 &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;amp;(*heapInt)&lt;/span&gt;&lt;/tt&gt; 取到的地址和 malloc 回傳的地址是相同的。&lt;/p&gt;
&lt;p&gt;接著我們試著把這種個程式的 Call stack 畫出來看看……記得 Stack 和 Heap 是不同的東西，所以要分開來畫喔。&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch2/01.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch2/01.png" src="http://bone.twbbs.org.tw/Attachments/ch2/01.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch2/02.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch2/02.png" src="http://bone.twbbs.org.tw/Attachments/ch2/02.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch2/03.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch2/03.png" src="http://bone.twbbs.org.tw/Attachments/ch2/03.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch2/04.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch2/04.png" src="http://bone.twbbs.org.tw/Attachments/ch2/04.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;記憶體洩漏&lt;/h2&gt;
&lt;p&gt;到了這裡，不知道你有沒有發現上面的四張圖有點怪怪的呢？&lt;/p&gt;
&lt;p&gt;沒錯，在第四張圖裡面，我們的 requireHeap() 函式返回之後，用來指到程式語言 runtime 給我們的記憶體用的 &lt;strong&gt;heapInt&lt;/strong&gt; 變數已經消失了，但是記憶體位址 8540176 的地方還存著一個整數 10，但卻沒有人可以用到那塊記憶體！&lt;/p&gt;
&lt;p&gt;想想看，如果我們在 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 裡面呼叫了 requireHeap 一百次的話，或發生什麼事情呢？&lt;/p&gt;
&lt;p&gt;嗯……你答對了，Heap 上被挖出了一百個整數十的洞，可是接下來卻沒有任何人可以用到這些你挖出來的記憶體空間。&lt;/p&gt;
&lt;p&gt;這個狀況，我們就叫他記憶體洩漏，或 &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Memory_leak"&gt;Memory Leak&lt;/a&gt;，因為我們的記憶體就像壞掉的水龍頭一樣，在不知不覺當中一點一滴的流失掉了。&lt;/p&gt;
&lt;p&gt;下面是如果我們在 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 中呼叫 &lt;tt class="docutils literal"&gt;requireHeap()&lt;/tt&gt; 三次之後，在 &lt;tt class="docutils literal"&gt;main()&lt;/tt&gt; 函式返回讓程式結束掉之前的記憶體狀態，可以看見被挖掉了三個整數的空間。&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch2/05.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch2/05.png" src="http://bone.twbbs.org.tw/Attachments/ch2/05.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="c"&gt;
&lt;h2&gt;C 語言如何釋放記憶體&lt;/h2&gt;
&lt;p&gt;上面這種「記憶體一點一點被吃掉」的情況當然是我們所不樂見的，所以我們必須在使用完 Heap 之後將記憶體還給程式語言 runtime ，讓他可以被其他人使用才行。&lt;/p&gt;
&lt;p&gt;在 C 語言裡面要達成這件事，靠的就是 &lt;tt class="docutils literal"&gt;free(void *)&lt;/tt&gt; 這個函式，我們只要傳給他當初 malloc 回傳給我們，指到的 Heap 開頭的指標就好。&lt;/p&gt;
&lt;p&gt;所以我們修改我們的 requireHeap() 函式如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;requireHeap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;

    &lt;span class="n"&gt;heapInt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;heapInt: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;*heapInt: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heapInt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;*heapInt: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;amp;(*heapInt): %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heapInt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;這樣的話，現在我們的 Call Stack 會長這個樣子：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch2/01.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch2/01.png" src="http://bone.twbbs.org.tw/Attachments/ch2/01.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch2/02.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch2/02.png" src="http://bone.twbbs.org.tw/Attachments/ch2/02.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch2/03.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch2/03.png" src="http://bone.twbbs.org.tw/Attachments/ch2/03.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch2/06.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch2/06.png" src="http://bone.twbbs.org.tw/Attachments/ch2/06.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://bone.twbbs.org.tw/Attachments/ch2/07.png"&gt;&lt;img alt="http://bone.twbbs.org.tw/Attachments/ch2/07.png" src="http://bone.twbbs.org.tw/Attachments/ch2/07.png" style="width: 500px;" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;關於 C 記憶體操作的一些小陷阱&lt;/h2&gt;
&lt;p&gt;如果你在上面的程式當中，試著在把 heapInt 給 free 掉後，又用 *heapInt 來取得值，可能會發現他可以正常執行，但是讀出來的值可能怪怪的，例如在我的機器上讀出來會是 0。&lt;/p&gt;
&lt;p&gt;這是因為 C 語對於存取已經 free 掉的 Heap 空間的定義是 undefined，也就是說只有神才知道發生什麼事。&lt;/p&gt;
&lt;p&gt;但因為這塊記憶體空間已經被宣告為 free 了，也就是說他隨時都會配置給其他人，其他人隨時都可能寫資料到這塊記憶體上把你的資料蓋掉，所以你&lt;strong&gt;絕對不應該存取已經被 Free 掉的空間&lt;/strong&gt; 。&lt;/p&gt;
&lt;p&gt;另外，如果你試著呼叫兩次 free(heapInt) 則程式會馬上當掉，這是因為雖然這樣的行為也是 undefined，但大部份的 C 語言的 runtime 會做檢查，在發生這件事時把你的程式結束掉。例如若你是在 Linux 下，glibc 就會丟出 &lt;tt class="docutils literal"&gt;double free or corruption (fasttop)&lt;/tt&gt; 這個錯誤。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h2&gt;小結&lt;/h2&gt;
&lt;p&gt;這一次我們講的是 Heap 的基本觀念，以及存放在 Heap 上的資料的特性，總結如下：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Heap 像是由程式語言 runtime 管理的銀行，你可以向他要記憶體和還記憶體&lt;/li&gt;
&lt;li&gt;要操作 Heap 上的資料，靠的是指到該 Heap 的記憶體位置的指標&lt;/li&gt;
&lt;li&gt;在 C 語言要到的 Heap 空間一定要 free 掉，不然會有記憶體洩漏&lt;/li&gt;
&lt;li&gt;已經被 free 掉的 Heap 隨時可能分配給其他人，絕對不要再去存取他。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;有了這些基本的概念，接下來我們終於可以開始討論 Java 了！下一章我們會討論 Java 怎麼樣使用 Heap 來存放物件，還有他怎麼對付 Memory Leak 的問題。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id8"&gt;
&lt;h2&gt;參考資料&lt;/h2&gt;
&lt;p&gt;你可以在下面找到 Java 對於詳細記憶體操作的規範，還有 C 的作者之一寫的 The C Programming Language，兩個都是當你想要詳細了解 C / Java 對了你的記憶體幹了什麼好事時的很好的參考資料：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://docs.oracle.com/javase/specs/"&gt;Java SE Specifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.amazon.com/C-Programming-Language-2nd-Edition/dp/0131103628"&gt;The C Programming Language&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="id9"&gt;
&lt;h2&gt;目錄&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-16-candjavamemory.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（一）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;淺談 C 語言和 Java 的記憶體管理（二）&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory3.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（三）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-18-candjavamemory4.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（四）&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">墳墓 (Brian Hsu)</dc:creator><pubDate>Tue, 17 Apr 2012 18:40:00 +0800</pubDate><guid>http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory2.rst.html</guid><category>C</category><category>Java</category><category>記憶體管理</category></item><item><title>淺談 C 語言和 Java 的記憶體管理（一）</title><link>http://bone.twbbs.org.tw/blog/programming_2012-04-16-candjavamemory.rst.html</link><description>&lt;div class="section" id="id1"&gt;
&lt;h2&gt;目錄&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;淺談 C 語言和 Java 的記憶體管理（一）&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory2.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（二）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory3.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（三）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-18-candjavamemory4.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（四）&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;話說現在寫程式已經不是資訊系的專利了，只要隨便一個路人突然想不開，他就可以跑進書店去買一本什麼 Java 啦、C# 啦或是 Ruby on Rails 的書，然後搖身一變成為一個天天加班的苦命 Web Developer 或是軟體工程師。&lt;/p&gt;
&lt;p&gt;這都是拜這些程式語言或框架所賜，幫我們把電腦的底層運作抽象化，並且替我們簡化了非常多的事情，讓我們這些寫程式的人可以專注在思考程式的邏輯，而不是繁瑣的細節操作。&lt;/p&gt;
&lt;p&gt;我覺得這是一件好事，但這件事同時也是一體兩面的－－抽象化的愈厲害，你愈難知道你的程式究竟在你的電腦做了哪些事情，當你的程式不如預期的運行的時候，你愈難肯定是這一層一層的抽象化中的哪個環節出錯了。&lt;/p&gt;
&lt;p&gt;所以我也一直覺得，寫程式的人最好理解你用的程式語言到底幫你做了哪些事情，而其中一個很重要的問題就是－－你的程式到底對記憶體做了哪些好事。&lt;/p&gt;
&lt;p&gt;畢竟十之八九的程式碼，在做的事情就是在記憶體中挖一塊出來操作，設定一些變數，對那些變數加加減減之類的。&lt;/p&gt;
&lt;p&gt;所以知道你的程式碼在某個時候對記憶體做了些什麼，可以讓你有一個基本的武器，在事情出錯的時候判斷到底是發生了什麼事情，或是在有的網站上看到有人說 Java is call by reference 這句話的時候，知道這句話根本就是狗屁不通。&lt;/p&gt;
&lt;p&gt;因為好像沒有看到比較完整的、簡介性的東西，而這些東西在坊間教你寫程式的書本也很難找到。會講到這類問題的書籍大概主要是 OS 或是 System Programming 的書，但這些書對於只會寫基本的 C / Java 程式的人可能不是很容易懂，所以乾脆自己跳下來寫一篇從 C / Java 的 Programmer 來看記憶體的操作算了。&lt;/p&gt;
&lt;p&gt;雖然因為我比較熟的語言只有 C 和 Java，所以這篇是講 C 和 Java 的記憶體管理和運作，但我強列建議您最好也試著去了解，自己所慣用的程式語言對於記憶體到底是怎麼做操作的。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;記憶體是什麼&lt;/h2&gt;
&lt;p&gt;呃……既然都會寫 C 和 Java 程式了，應該不用講記憶體是什麼了吧？&lt;/p&gt;
&lt;p&gt;這邊要強調的，就只有是記憶體在抽象的概念上可以把他想象成是一塊連續的儲存空間，而且你可以透過 Memory Address 來做定位。&lt;/p&gt;
&lt;p&gt;例如你可以說我要取得從記憶體開頭算來的第 40 個 byte 到第 80 個 byte 之間的空間這樣（這部份我簡化非常多，真的有興趣可以參考 OS 的書）。&lt;/p&gt;
&lt;p&gt;有了以上的基本概念，才能理解 C 的 Pointer 和 Java 的 Reference 到底是什麼東西，以及為什麼會有 Memory Leak 的情況發生。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h2&gt;斯斯有兩種，記憶體也有兩種&lt;/h2&gt;
&lt;p&gt;雖然記憶體實際上是一大塊的連續空間，但由於電腦程式的運作方式，當我們從程式語言的角度來看時，會把記憶體分為以下的這兩大用途：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Stack&lt;/li&gt;
&lt;li&gt;Heap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要注意的是這裡的 Stack / Heap 和你在資料結構所學的 Stack / Heap 不一樣，單純是由於程式運行時的特性而給的名稱，這也是為什麼我不把他翻成中文的原因，請把你學過的資料結構忘掉！這裡的 Stack 和 Heap 就單純是記憶體的空間！&lt;/p&gt;
&lt;p&gt;懂得將記憶分類為 Stack 和 Heap 兩種不同的類型是很重要的一件事，因為你程式的資料是存在 Stack 或是 Heap 上，對於他們什麼時候可以被使用，什麼時候不能用有非常重大的影響，所以請跟著我唸五次：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;記憶體分成 Stack 和 Heap！&lt;/li&gt;
&lt;li&gt;記憶體分成 Stack 和 Heap！&lt;/li&gt;
&lt;li&gt;記憶體分成 Stack 和 Heap！&lt;/li&gt;
&lt;li&gt;記憶體分成 Stack 和 Heap！&lt;/li&gt;
&lt;li&gt;記憶體分成 Stack 和 Heap！&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這句話是一切的重點，是內功心法第一零一招，通常只要你知道了你的資料到底是存在 Stack 還是 Heap 上，並且能夠在腦海或紙上畫出 Stack 和 Heap 的長相的時候，有高達八成的問題都可以釐清！&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="stack"&gt;
&lt;h2&gt;Stack 與區域變數&lt;/h2&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;這節用的都是 C 語言的範例，但如果你只會 Java 語言也沒關係，程式碼都很簡單，應該可以很容易看得懂。更重要的是，雖然 Java 程式是運行在 VM 上，但在這部份的概念是一模一樣的，你就把裡面的 C 程碼式全當成是 Java 的 static method 就好。&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;既然知道記憶體可以分為兩類，那就讓我們先來看看第一類的 Stack 到底是什麼東西。&lt;/p&gt;
&lt;p&gt;請回想一下你寫程式的時候，是不是會把某些小功能寫成 C 的函式或 Java 的方法，然後在需要的時候才去呼叫那個函式做事情呢？&lt;/p&gt;
&lt;p&gt;而通常在這個時候，你的函式裡面也會宣告一些區域變數來做暫時的運算，你有沒想過，在程式執行的時候，那些變數到底是存在哪裡的？&lt;/p&gt;
&lt;p&gt;這一節，就是要回答上面這個問題。&lt;/p&gt;
&lt;p&gt;首先，我們來看一個非常簡單的 C 程式碼：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c1"&gt;// filename: test.c&lt;/span&gt;

&lt;span class="cp"&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include &amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;function1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Inside function1&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;function2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Inside function2&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;function3&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Indeise function3&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;function1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;function2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;End of function3&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;function1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;function2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;function3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;function2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;function1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;以上的程式應該非常簡單，他的輸出長得如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="go"&gt;brianhsu@USBGentoo ~ $ gcc test.c&lt;/span&gt;
&lt;span class="go"&gt;brianhsu@USBGentoo ~ $ ./a.out&lt;/span&gt;
&lt;span class="go"&gt;Inside function1&lt;/span&gt;
&lt;span class="go"&gt;Inside function2&lt;/span&gt;
&lt;span class="go"&gt;Indeise function3&lt;/span&gt;
&lt;span class="go"&gt;Inside function1&lt;/span&gt;
&lt;span class="go"&gt;Inside function2&lt;/span&gt;
&lt;span class="go"&gt;End of function3&lt;/span&gt;
&lt;span class="go"&gt;Inside function2&lt;/span&gt;
&lt;span class="go"&gt;Inside function1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;他的結果完全符合我們的預期，照順序印出我們想看到的字樣。&lt;/p&gt;
&lt;p&gt;咦？這和 Stack 有什麼關係嗎？別急，請從 main() 開始畫起，照著程式的流程走一次，每遇到一個 function call 就把該 function 的名字加在現在的圖上，而每一個函式返回的時候，就把那個函式的名字擦掉，最後你會生出像下面一樣的圖（點圖可放大）：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="https://lh5.googleusercontent.com/-pXzpUueVbEM/T4vRWLixG9I/AAAAAAAANco/WAdwIwhOllg/s800/stack.png"&gt;&lt;img alt="Call Stack" src="https://lh5.googleusercontent.com/-pXzpUueVbEM/T4vRWLixG9I/AAAAAAAANco/WAdwIwhOllg/s800/stack.png" style="width: 600px;" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;Call Stack 範例&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;上面的這個圖就叫 Call Stack，有沒有發現他的特性和我們在學資料結構時遇到的堆疊非常像呢？&lt;/p&gt;
&lt;p&gt;上面的那一格格一格的東西，就是你的程式在執行的時候，被保留下來的記憶體空間。&lt;/p&gt;
&lt;p&gt;當你每呼叫一個函式的時候，系統就會幫你多保留一塊的記憶體給你的函式用，而當你的函式結束的時候，原本保留給你的記憶體空間就會被標成可以給其他人用，於是你的圖看起來就會像是一個 Stack 一樣。&lt;/p&gt;
&lt;p&gt;而圖中的每一格和每一格都是相臨的，也就是當你呼叫 function1() 的時候，他配置給 function1() 的空間會緊接著在給 main() 的後面。&lt;/p&gt;
&lt;p&gt;那這個時候，如果我們在 function1 裡加了一個整數變數，把我們的 function1 改成像下面這樣，會發生什麼事情呢？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;function1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Inside function1&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其實很簡單，就是在上面被保留起來給 function1 的記憶體會稍微大一點，讓你可以放下那個整數變數。&lt;/p&gt;
&lt;p&gt;所以現在我們的 Call Stack 圖型就會變成像下面這樣：&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="https://lh3.googleusercontent.com/-zT6nYFzVUM0/T4vRWM9Ly5I/AAAAAAAANco/wCSgyNuPOFY/s800/stack2.png"&gt;&lt;img alt="Call Stack" src="https://lh3.googleusercontent.com/-zT6nYFzVUM0/T4vRWM9Ly5I/AAAAAAAANco/wCSgyNuPOFY/s800/stack2.png" style="width: 600px;" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;具有區域變數的程式 Call Stack 範例&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;如果仔細觀察上面的圖的話，你會發現以下兩件事：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;雖然都都是 function1 裡的 x 變數，但每次 function1 執行的時候，x 所在的位置是不一定的，都會被重新配置一份。&lt;/li&gt;
&lt;li&gt;function1 結束後，x 變數就無法被取得。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上兩件事情，就是在 C 和 Java 程式當中，存放在 Stack 中的資料會具有的特性。&lt;/p&gt;
&lt;p&gt;現在回過頭來看，你可能常常在講 C 或 Java 的書上看到「在 C / Java 裡區域變數的生命週期只有在函數執行時」這句話，現在應該就變得很好理解了－－因為函數結束後，存放你的變數的那塊記憶體可能早就已經被其他人給蓋掉了，所以你的變數當然不會繼續活著囉（不然就天下大亂了）。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id8"&gt;
&lt;h2&gt;小結&lt;/h2&gt;
&lt;p&gt;在這一篇文章中，我們提到了以下幾個重點：&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;記憶體分成 Stack 和 Heap 兩種用途&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;在 C / Java 裡的區域變數，其內容都是存在 Stack 上，而這樣的變數有以下的特性：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;每次執行到該函式時，變數所佔的記憶體空間都會被重新配置一次，而且位置是不一定的。&lt;/li&gt;
&lt;li&gt;函式結束的時候，該變數就被視為「死亡」，不可以再做存取，原先放那個變數的記憶體空間，也可能被其他的資料蓋掉。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;區域變數和 Stack 的關係大致上就是這樣，也是寫 C / Java 的時候應該要注意的一件事情，特別當你是應用遞迴寫程式的時候－－因為你每遞迴呼叫一次你的函式，你的 Stack 記憶體上面就會多一層，假設你遞迴呼叫了一百次我們的 function1，那就會出現一百個不同的 x 變數，佔掉你 100 個整數的空間。&lt;/p&gt;
&lt;p&gt;這件事乍看之下沒啥重要的，但如果你的遞回非常深，你的 Stack 就會爆掉，例如以下的 Java 程式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;deep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;deep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;deep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;現在可以了解為什麼有的時候用 Java 寫遞迴的程式出錯的時候，Java 就會丟個 java.lang.StackOverflowError 出來，然後把程式結束掉了吧？&lt;/p&gt;
&lt;p&gt;答案就是因為你的函式一直配置新的空間，導至整個 Stack 爆掉了。&lt;/p&gt;
&lt;p&gt;下一篇文章，我們會來看看什麼是 Heap，還有 Heap 有什麼特性。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id9"&gt;
&lt;h2&gt;目錄&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;淺談 C 語言和 Java 的記憶體管理（一）&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory2.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（二）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-17-candjavamemory3.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（三）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://bone.twbbs.org.tw/blog/programming_2012-04-18-candjavamemory4.rst.html"&gt;淺談 C 語言和 Java 的記憶體管理（四）&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">墳墓 (Brian Hsu)</dc:creator><pubDate>Mon, 16 Apr 2012 18:31:00 +0800</pubDate><guid>http://bone.twbbs.org.tw/blog/programming_2012-04-16-candjavamemory.rst.html</guid><category>C</category><category>Java</category><category>記憶體管理</category></item></channel></rss>
