1. Tips: 遠端桌面連線的小技巧

    查了文件, 才發現可以這樣用... 平常連到 server 用的遠端桌面連現, 常碰到幾個問題:

    1. 每次都要打 IP, 能不能拉捷逕出來, 我常連的那台只要點兩下就自動登入?
    2. 只有那幾種解析度可以選, 沒有我要的...
    3. 遠端桌面連進去的畫面, 跟本機的不一樣. 看不到某些在本機才看的到的訊息...

    原來這些都有解啊... (1) 最簡單, 把設定存檔就好, 就附圖的資訊, 底下有 [Save As], 以後直接點兩下存好的檔案就好了.

    image

     

    再來, (2) 跟 (3) 其實也有解, 只要先打開 DOS Prompt, 輸入  MSTSC /? 就會出現這個說明畫面:

    image

     

    答案就在影片中... 加上 /w:1440 /h:900 參數, 就可以用寬螢幕的解析度 1440 x 900 來搖控遠端的 server 了. 想要看 console (本機) 的畫面嘛? 比如有時 service 的 error message 只會秀在 console.. 這時只要加上 /console 參數就好. 整段指令如下:

    image

    開出來的視窗:

    image

    嗯, 看寬螢幕的果然比較爽, 當然這樣也就有機會用雙螢幕了. 小技巧, 需要的人可以參考看看!

    2008/03/06 Tips 技術隨筆

  2. Memory Management (III) - .NET CLR ?

    上篇 & 上上篇 ,同樣的問題,我改用 .NET 開發是不是就搞定了? 其實這篇才是我要寫的重點,只不過引言寫太高興,就是兩篇文章了,咳咳… 有人在問,為什麼我老是寫些冷門的文章? 沒辦法… 大家都在寫的東西我就沒興趣寫了,文筆沒別人好,網站沒別人漂亮,連範例程式都沒別人炫,只好挑些沒人寫的內容…

    大部份討論這主題的文章,講的都是 GC, GC 的 generation,IDisposable,還有 Heap 等等,不過這些知識都無法直接回答這次問題。底下的例子你會發現,預設的 GC 也無法解決 memory fragment 的問題,不過實際上是有解的,只是還要動用到秘技…

    回題,先來看看之前的問題為什麼會是個問題? 萬惡之首都在: 指標 (POINTER)。

    因為有 pointer,因此 C 絕對不能 自動 幫你調整記憶體位置,也就一定會有這種問題。看到為何我在上篇提到的程式碼要把 pointer 的值印出來? 因為這代表我可以輕易拿的到實際的位址,因此任何重新定址 (relocation) 的動作一定會影響到程式的執行。所以最根本的解決辦法就是把 pointer 這東西拿掉。

    年紀較輕的程式語言,如我常提到的 Java 跟 C#,都完完全全的把 pointer 從語言內移掉了,只留下 reference 這類差不多的東西。除了拿不到絕對的 address 之外,其它功能一個都不缺。但是這樣帶來的好處是很明顯的,除了一般書上講到爛的理由: “更安全,更簡易” 之外,很重要的一點就是,像 CLR or JavaVM 這種環境,開始有機會去搬移記憶體配置的區塊,徹底的由系統層面解決這種問題了。

    .NET / Java 回收記憶體的動作是自動的,就是常聽到的 Garbage Collection,而上面提到的 relocation,就是指在回收時順便把剩下已配置的空間排在一起,搬移記憶體區塊所需要的重新定址動作。這種類型的 GC 有個特別的名辭,叫作 compact collection。理論上,.NET 已經具備這樣的條件了,應該要有能力可以解決這樣的問題。

    不過 “可以解決” 跟 “已經解決” 仍然有一段差距,那麼現在的 .NET CLR 到底行不行? 一樣的範例程式用 C# 改寫一下,同樣的試看看,不過這次懶的再放好幾種版本試試看了,除了最大可用記憶體可能有差別之外,其它應該都統一了。我只針對 .NET 2.0 (x86) 一種版本測試,一樣,鐵齒的讀者們,有興趣就抓回去試一試…。

    整段程式碼跟之前 C 版本大同小異,就是照順序配置 64mb 的 byte[],直到丟出 OutOfMemoryException,然後跳著釋放,接著再配置 72mb 的 byte[],看看能不能配置成功? 直到再丟出 OutOfMemoryException 為止,能配置多少記憶體? 這邊為了方便,我直接在 vista x86 系統上測試:

    測試的結果令我想殺人,竟然是 FAIL ? 放掉的空間拿不回來…

    後來想到,程式移除 reference,不見得會立刻釋放記憶體,總得等垃圾車 (Garbage Collect) 來收拾一下… 手動呼叫了 GC,也強迫指定要回收所有的 Generation 了 (呼叫: GC.Collect(GC.MaxGeneraion) ) 再試一次:

    結果好不到那裡去,難到我沒用市政府的垃圾袋嘛? [:@] 查了一下 MSDN,常見的 generation 問題也試過,沒有用。90% 講 CLR GC 的問題都在探討 generation 的問題…  查到某 Java 名人的 文章,提到了 compact collection 比較接近,不過沒有講怎麼明確的啟動這樣的 GC 啊… 後來去翻 .NET runtime 裡關於 garbage collection 的設定,發現還有這玩意… gcConcurrent / gcServer:

    gcConcurrent: Specifies whether the common language runtime runs garbage collection on a separate thread.

    gcServer: Specifies whether the common language runtime runs server garbage collection.

    講的很清楚,不過對我沒啥用。gcConcurrent可能的影響是,也許呼叫後系統還在GC,我的程式就先跑下去了? 因此這東西關掉也許有幫助,再來試一次:

    真慘,一點幫助都沒有… 放掉的 768MB,只撈回 72MB,再來看一下最後一個 gcServer,看它的 HELP 看不大出來什麼是 “server garbage collection” ? 算了,試一下比較快:

    Bingo,看來這個參數下下去才是我預期的結果,放掉了 576MB,後面撈了 648MB 回來。這樣的作法,已經完全不會受到 memory fragment 問題的影響,証實了 compact collection 是有發恢它的效用的,只不過這個參數實際的作用,翻遍了 Google / MSDN,得到的都是很模菱兩可的答案,不外乎是你的程式如果是 blah blah blah 的話就要用 gcServer,這樣會比較好之類的,不過實際的差別則看不大出來。沒有任何一篇文件明確提到 server gc 會做 compact collection (如果這篇不算的話,哈哈),而 workstation gc 不會,也許前面的方式也會觸發 compact collection也說不定,只是時機不成熟…

    抱著不可能的希望,用 Reflector追看看,果然不出所料,Reflector也看不到細節,因為全都呼叫 native code 去了。不過這次的測試,至少確定了,在啟用 gcServer option 之後,CLR 的 GC 是會進行 compact collection 的。

    寫到這裡,本系列文章結束,只是為了在新的平台驗證古早的問題而以,果然時代在進步,以前耽心的問題現在都不再是問題了。這一連串試下來,學到了一課,原來 gcServer 有這個差別,算是值回票價了。最後把我的測試程式碼貼一下,一樣,歡迎拿去各種平台試一下,有不一樣的結果也記得通知我一聲!

    [Program.cs]

    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    namespace ClrMemMgmt
    {
        class Program
        {
            static void Main(string[] args) {
                List<byte[]> buffer1 = new List<byte[]>();
                List<byte[]> buffer2 = new List<byte[]>();
                List<byte[]> buffer3 = new List<byte[]>();
                
                //            
                //    allocate             
                //            
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("1. Allocate 64mb block(s) as more as possible...");
                try
                {
                    while (true)
                    {
                        buffer1.Add(new byte[64 * 1024 * 1024]);
                        Console.Write("#");
                        buffer2.Add(new byte[64 * 1024 * 1024]);
                        Console.Write("#");
                    }
                }
                catch (OutOfMemoryException)
                {
                }
                Console.WriteLine();
                Console.WriteLine("   Total {0} blocks were allocated ( {1} MB).", (buffer1.Count + buffer2.Count), (buffer1.Count + buffer2.Count) * 64);
                
                //        
                //    free  
                //        
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("2. Free Blocks...");
                buffer2.Clear();
                Console.WriteLine("   Total: {0} blocks ({1} MB)", buffer1.Count, buffer1.Count * 64);
    
                //        
                //  GC  
                //            
                GC.Collect(GC.MaxGeneration);  
                     
                //           
                //    allocate  
                //          
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("3. Allocate 72mb block(s) as more as possible...");
                try
                {
                    while (true)
                    {
                        buffer3.Add(new byte[72 * 1024 * 1024]);
                        Console.Write("#");
                    }
                }
                catch (OutOfMemoryException)
                {
                }
                Console.WriteLine();
                Console.WriteLine("   Total: 64mb x {0}, 72mb x {1} blocks allocated( {2} MB).\n", buffer1.Count, buffer3.Count, buffer1.Count * 64 + buffer3.Count * 72);
                Console.ReadLine();
            }
        }
    }
    

    [configuration file]

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>  
      <runtime>    
        <!--<gcConcurrent enabled="false" />-->    
        <!--<gcServer enabled="true" />-->  
      </runtime>
    </configuration>
    

    2008/03/03 系列文章: Memory Management .NET 作業系統 技術隨筆

  3. Memory Management (II) - Test Result

    該來揭曉謎底了,在人氣不怎麼高的地方拋這些冷門的問題,看的我都覺的 “這版主真是不自量力” .. 咳咳.. 為了把之前的心得,在現在的主流作業系統及平台再驗證一次,只好自己花了點小工夫,把 C 挖出來寫個測試程式,不過 C / C++ 實在太久沒寫了,已經忘到連語法都得翻 HELP 的程度 :~ 花了些時間才搞定。

    不過也因為這樣,連帶的查了一下如何編譯 x64 的程式碼,還有一些相關的設定項目。這次測試只測了 windows 的環境,沒辦法,這把年紀實在沒力氣再去摸第二種 OS 了,如果有善心人事要幫我測的話,我倒是很樂意把你測的結果一起放上來! 程式碼請看 [註3]

    不多說廢話,測試主要會針對三種環境來測試:

    1. 一般的 x86 (win32)
    2. 在 x64 下的 32 位元執行環境 (wow64)
    3. 原生的 x64 程式

    原本還想加上 /3GB options 來測的,不過這樣跟 (2) 幾乎是一樣的,只差在 3GB 跟 4GB 的可用空間而以,差異不大,當然就省下來了 [H]

    測試結果就直接抓畫面附在底下了。程式碼當然都是同一份。原始 project 放在 [這裡]。其實這次問題的關鍵,跟 windows / linux,32/64,都沒有直接關係,唯一有關的是,你的 memory address space 到底有沒有用完… 怎麼說? 先來看結果:

    1. x86 build (run under windows 2003 standard x64):
    2. x86 build + /LARGEADDRESSAWARE option (under 2003 x64)
    3. x64 build

    看不出個所以然? 簡單畫個表格整理一下測試結果:

    測試環境統一為 Windows 2003 x64 版本,可用記憶體為 2GB,分頁檔有 4GB。

    測試項目 測試 #01 測試 #02 測試 #03
    執行環境 32Bits (WOW64) 32Bits (WOW64) 64Bits (Native)
    Build Options x86 x86 + LargeAddressAware x64
    可定址空間 / 實際可用空間 2048MB / 1920MB 4096MB / 3904MB 8TB / 4032MB
    問題的測試結果 / 可以配置的 72mb 區塊 NO / 2 NO / 2 YES / 27

    這結果大概會跌破一堆人的眼鏡,雖然板上回應的很冷清,不過私下 MSN 問了幾個人,有很肯定的,也有不確定的,當然也有亂猜猜的很肯定的 (S*M,就是你…),不過答案都很兩極,不是都不行,不然就是都可以…

    就理論上來說,分頁的記憶體管理方式,的確是不能解決在 virtual memory address space 過度分散 (fragment) 的問題,如果程式或作業系統沒有作適度的 defrag,那麼你要挖一塊記憶體的要求一定會失敗,但是為什麼測試 #03 會通過? 因為實際可用的 Memory ( Physical RAM + Swap File ) 也不過 6GB,你的程式需索無度,要求超過的怎麼樣也生不出來。6GB 扣掉其它 OS / AP 用掉的,跟理論上能用的 8TB 實在是差太多,造成你的 virtual memory address space 跟本不可能用完。當然 不可能 這字眼別用太滿,十年前也是認為 4GB 跟本不可能用完,不過我現在的 PC 就已經裝了 6GB … [:$] 上表中列了一些較特別的參數,像是明明是 32 位元,怎麼可定址空間是 2048MB ? 還有 LargeAddressAware 是什麼? 這些屬 windows 較特別的規矩,我在文章最後面的 [註1] 說明一下。

    現在的 PC,隨便都是 1GB / 2GB RAM,加上分頁檔,超過 4GB 跟本是件很普通的事,意思是寫不好的程式,的確是已經會面臨到這樣的困境了,明明記憶體還夠用,但是系統卻回報 Out Of Memory …。只可惜這樣的問題,OS一點忙都幫不上。因為 OS 沒有辦法替你做 Memory Defragment 的動作 [註2]。上一篇 我有畫張記憶體配置的圖,只能用來說明 #01 / #02 的情況,換成 #03 就不大適用了。只要可定址空間夠大,基本上你只需要考慮你配置的記憶體總量有沒有超過可用的記憶體就好,是不大需要在意是不是有 fragment 的問題,除非你的可配置空間跟可用空間很接近,這問題才會浮現出來。就像積木買來,積木的體積跟盒子差不多大,你要全擺進去就得花點工夫安排一下,否則一定會有一些是收不進去的一樣 (幫吳小皮收玩具的感想… -_-)。不過如果你換一個大盒子,或是把整個房間當成大盒子來看,隨便丟都沒問題,連會不會撞在一起都不用耽心,一定不會有空間夠卻放不進去的問題,這就是差別。

    這測試結果看起來很可怕,感覺起來只要是 32 位元的程式,加上是 server or services,會長時間運作的,好像都有可能碰到這種問題。這不算是 Memory Leak (因為記憶體是真的有成功的被釋放),那麼 Java / .NET 宣稱的 Garbage Collection 回收機制到底會不會碰到這個問題? 別耽心,等著看下一篇就知道了 XD

    註 1. /LARGEADDRESSAWARE:

    32 位元系統可定址空間應該是 2^32 = 4GB 沒錯,不過 Microsoft 把它一分為二,規劃一半是給 Kernal,另一半才是給 APP 使用。意思是你的程式再怎麼用只能用到 2GB。不過這種問題幾年前就浮現出來了,Microsoft 特地在 OS 上加了 /3GB 這種參數,可以把 OS : AP = 2GB : 2GB 的規劃,調整為 1GB : 3GB。不過相對的程式在編譯時也要加上 /LARGEADDRESSAWARE 參數,才有可能讓 AP 取得 2GB 以上的記憶體。

    所以即使在 x64 下執行的 x86 應用程式,跟本沒有 OS 那 2GB 的需求,一般 x86 APP 在 64 位元作業系統下仍然只能用到 2GB,但是不同參數編譯出來的程式碼,就能用到 4GB (如果是在加上 /3GB 的 x86 OS,則大約是 3GB)

    註 2. 為什麼 OS 不能替 Memory 執行 Defragment 的動作?

    原因很簡單,測試的程式是用 C / C++ 這類可以直接存取 pointer 的程式語言寫的。試想一下 OS 如果替你把已配置的記憶體區塊挪一挪會發生什麼事? 你拿到的 pointer 全都成了廢物,因為它指向的記憶體,可能已經不是當時你認識的資料了… 因為資料有可能被強迫搬家,你的通訊錄再也沒有用,老朋友就失聯了…

    因此,別的程式語言不一定,但是允許你直接使用 pointer 的語言,這類的問題你閃不掉,一定得自己想辦法…

    註 3. 測試程式碼

    這段 code 我做了點改變,因為 4kb block size 實在太小了 (相對於 4GB 上限),印出訊息一大堆,執行又慢,因此我自己把問題的參數調整一下,把問題的 4kb 改成 64mb,而最後要配置的 5kb 則改成 72mb。若對這段 code 有興趣的人,可以直接 copy 去用。我直接把 source code 貼在底下 ( C++ 語法忘了一大半 :P,因此用的都是單純的 C … 除了 conio.h 之外,應該隨便都能成功編譯吧,看誰有興趣可以拿到 Linux 下去試看看….):

    #include <stdio.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <malloc.h>
    
    void init_buffer(void *ptr, unsigned int size) {
        if (ptr == NULL) return;
    
        //for (int count = 0; count < size / sizeof(long); count++) {
        //    ((long *)ptr)[count] = 0;
        //}
    }
    
    int main(const int&amp; args) {
        void *buffer1[4096];
        void *buffer2[4096];
        void *buffer3[4096];
    
        //
        //    allocate
        //
        printf("\n\n");
        printf("1. Allocate 64mb block(s) as more as possible...\n");
    
        int total = 0;
        for (int count = 0; count < 4096; count++) {
            buffer1[count] = buffer2[count] = NULL;
            buffer1[count] = malloc(64 * 1024 * 1024);
    
            if (buffer1[count] == NULL) break;
            init_buffer(buffer1[count], 64 * 1024 * 1024);
            printf("#");
            total++;
    
            buffer2[count] = malloc(64 * 1024 * 1024);
    
            if (buffer2[count] == NULL) break;
            init_buffer(buffer2[count], 64 * 1024 * 1024);
            printf("#");
            total++;
        }
    
        printf("\n   Total %d blocks were allocated ( %d MB).\n", total, total * 64);
    
    
        //
        //    free
        //
        printf("\n\n");
        printf("2. Free Blocks...\n");
    
        for (int count = 0; count < 4096; count++) {
            if (buffer2[count] == NULL) break;
            free(buffer2[count]);
            buffer2[count] = NULL;
            total--;
            printf("#");
        }
    
        printf("\n   Total: %d blocks (%d MB)\n", total, total * 64);
        
    
        //
        //    allocate
        //
        printf("\n\n");
        printf("3. Allocate 72mb block(s) as more as possible...\n");
    
        int total2 = 0;
        for (int count = 0; count < 4096; count++) {
            buffer3[count] = malloc(72 * 1024 * 1024);
            if (buffer3[count] == NULL) break;
            printf("#");
            total2++;
        }
    
        printf("\n   Total: 64mb x %d, 72mb x %d blocks allocated( %d MB).\n", total, total2, total * 64 + total2 * 72);
        printf("\nDump Blocks Address:\n");
    
        for (int count = 0; count < 4096; count++) {
            if (buffer1[count] == NULL) break;
            printf("  64mb block ponter: [%08p] ~ [%08p] \n", buffer1[count], (long)buffer1[count] + 64 * 1024 * 1024);
        }
    
        for (int count = 0; count < 4096; count++) {
            if (buffer3[count] == NULL) break;
            printf("  72mb block ponter: [%08p] ~ [%08p] \n", buffer3[count], (long)buffer3[count] + 72 * 1024 * 1024);
        }
    
    
        _getch();
        return 0;
    }
    

    2008/03/03 系列文章: Memory Management .NET 作業系統 技術隨筆

  4. Memory Management - (I). Fragment ?

    程式越寫, 越覺的課本教的東西很重要… 最近碰到一些記憶體管理的問題, 想到以前學 C 跟 OS 時, 大家都有個理想..

    只要 OS 支援虛擬記憶體, 以後寫 code 都不用耽心 Memory 不夠…

    聽起來很合理, 虛擬記憶體本來就是讓開發人員省事的機制啊… 當然前提不超過硬體限制, 像是 32 位元的程式就不能超過 4GB. Virtual Memory 也帶來很多好處. 除了可以以硬碟空間換取記憶體空間之外, 因為 swap 需要的 paging 機制, 也間接的解決了 “PHYSICAL” Memory Fragment 的問題.

    怎麼說? 邏輯的記憶體位址, 對應到實體的記憶體位址, 不一定是連續的. 有點像是硬碟的一個大檔案, 實際上可能是散在硬碟的好幾個不連續區塊. 除了效能問題之外, 是沒有任何不同的.

    因為這堆 “便民” 的機制, 現在的程式設計師還會考慮這種問題的人, 少之又少. 有的還聽不懂你在問啥… 以前在 BBS 討論版看到一個問題, 印像很深刻, 算一算十來年了還記得… 把這問題貼一下, 主題就是 programmer 到底該不該耽心 memory fragment 的問題? 實驗的方式很有趣:

    1. 連續以固定 size (ex: 4KB) allocate memory, 直到沒有記憶體為止
    2. 開始 free memory. 不過是跳著釋放. 比如 (1) 取得的一連串記憶體, 只放奇數位子 1st, 3rd, 5th, 7th ….
    3. 挑戰來了, 這時應該清出一半空間了. 如果我再 allocate 5KB 的記憶體, OS 會成功清給我? 還是會失敗?

    簡單畫張圖說明,就像這樣:

    其中: (1) 就是在可用的定址空間內盡量塞,因為虛擬記憶體的關係,不管實體記憶體夠不夠,都能夠使用。 (2) 就是跳著釋放記憶體後的分佈情況。 (3) 圖上看來已經沒有能夠容納 “大一點” 區塊的空間了,那麼 [?] 這個區塊到底還放不放的下?

    來來來, 大挑戰… 這種程式千萬別在自己家裡亂玩… 也別在你按不到 reset 開關的電腦亂玩 (ex: 遠端連到機房的 server) … 前題是用 C / C++ 這類可以直接操作 pointer 的 language. OS 不限, 覺的 Linux 強就用 Linux, 喜歡 Gates 的就用 windows… 32 / 64 位元都可以…

    先賣個關子, 結果會是怎麼樣? 不同的 OS 會有不同的結果嗎? 64位元會有不同嗎? 有興趣可以試看看,懶的寫 code 也可以猜看看!

    2008/02/27 系列文章: Memory Management .NET 作業系統 技術隨筆 有的沒的

  5. 困難重重的 x64

    即使是做足了功課,還是敗下陣來... Orz

    之前 Vista x64 用的都還不錯,直到加上 4GB 之後,才是惡夢的開始... 之前貼了一篇 6GB 很爽的 POST,之前插上去只偵測到4.8GB,想說小事情,一定是 BIOS Remap沒打開的關係,果然一開就是 6GB,一切正常,就貼了 POST ..

    不過隔一兩天,想開個 MCE 來看電視,怎麼沒訊號? 查了半天確定線路都正常,才想到之前剛裝好不是都 OK 嗎? MCE 還列為重點測試項目之一,driver早都打聽好了,怎麼還會這樣? 就一個一個設定 rollback 試看看...

    搞半天,問題出在想都沒想到的地方... 我的 TV 卡,在Vista x64開了Memory Remap後就會出問題了。Device Manager沒有任何異狀,但是MCE就一直說訊號微弱... 跟本沒辦法看. Memory Remap 關掉就正常了。寫Mail去圓剛跟華碩的客服反應,果然再怎樣還是要買大廠的...

    ASUS 有回,不過沒用...

    圓剛? 沒人鳥我...

    雖然切回 4.8GB 還是戡用,不過多買的 4GB 只能當 2.8GB 不到,感覺有點鳥... 加上裝了 X64 有一半以上的軟體都是 X86 ... 看起來實在有點礙眼... 其實現階段用 X64 也是有些缺點的,第一就是很多軟體及 DLL 都要分兩套, x64 + x86,很佔空間。第二,一樣的程式 x64 吃的 memory 比較多,為何? 每個 Pointer 都多兩倍空間... 多少都會有影響... 第三,幾乎用到 COM 元件的都得靠 WOW,效能有點下滑... 所以暫時還是換回 x86 版了.

    換回 x86 vista 後第一件事就是試試電視卡,在 x86 mode 下開不開 REMAP 就都正常,看來 Driver 要負一點責任,不過工作忙,暫時就不理它了,下次擇日再挑戰一次 x64. Ram 裝太多果然還是有一堆問題,新問題是我的主機板 (ASUS P5B-E Plus) 如果開了 BIOS Remap,進 Vista 後只能看到 2048MB Ram @_@,關掉反而還有 2.8GB... 搞什麼飛機...

    又是搞了半天,確定無解,網路上很多人跟我一樣... 本想就讓它 2.8GB 吧,不過又讓我發現了個評價不錯的 Ram Disk 軟體: "Gavotte Ramdisk"

    評價不外呼是免費,沒有容量限制,很穩定等等,不過它有個特異功能倒是 RAM 插太多的人要試試... 它可以把像我這樣失去的 RAM 挖回來用!!

    真的蠻神奇的,只要 Vista 起用 PAE (Physical Address Extension),這套 RAM DISK 就能自動把 OS 不會抓來用的 RAM 當成 RAMDISK. 不過不開 BIOS Remap 就沒輒... 因此當場我的組態變成: RAM: 2GB, RamDisk: 4GB ...

    真的是 Orz,我要那麼大的 RamDisk 幹嘛? 像網路一堆人把 Page File 放到 RamDisk 上的作法又覺的有點蠢,雖然很多時後非得要 Page File 不可,不過把 RAM 不夠時某些 RAM 的資料搬到 DISK,而這 DISK 又是 RAM 模擬的,感覺就像是做了一堆白工... 算了,拔掉 2GB 吧,剩下 2GB 就當 TEMP 用,可以塞 TEMP 的就塞過去..

    看起來 x64 還是小問題多多,沒那個人生跟他耗的話,還是過一陣子再試試吧,反正照這情勢,RAM很快就會漲到不得不換 X64 的地步了,往好的方面想,這應該會加速廠商移到 x64 的腳步吧? [H]

    2008/02/26 技術隨筆 有的沒的 水電工