顯示具有 程式概念 標籤的文章。 顯示所有文章
顯示具有 程式概念 標籤的文章。 顯示所有文章

2012年11月12日 星期一

資料結構容量大小的調整

StringBuilder 和 StringBuffer 底層都使用char[]來做為資料儲存庫
而且常常需要調整容量大小
調整容量大小的方法就是配置一個容量更大的新的char[]
接著將舊資料複製過來後再拋棄舊的char[]
類似的調整過程也可能發生在使用陣列當作底層資料儲存庫的Java SE Collection

依OpenJDK的實作方式
當儲存文字超過底層資料儲存庫的容量時
會配置一個原本容量兩倍大的新char陣列,而底層的char陣列大小預設為16
實務上很少StringBuffer 或StringBuilder的物件最後只使用了16以內的char元素
要避免StringBuffer 或StringBuilder調整大小的動作,我們可以指定大小給其建構子

如ArrayList、Vector、HashMap和ConcurrenthashMap等Collections因使用陣列儲存資料
同StringBuffer 或StringBuilder,很容易在增加元素時引發調整大小的動作
需要額外的CPU週期來配置新陣列、複製舊陣列的資料,將來也需要回收舊陣列
以HashMap為例,預設建構子的容量是16筆資料,也是經常不夠用
其資料擴張時也是使用原本陣列2倍大的新陣列
又因每次調整時都要計算雜湊(hash)值,當資料量大時耗費的成本也更驚人
遇到這種情形時,比較好的處理方式就是直接將計算容量的算式傳給HashMap的建構子

2012年6月27日 星期三

[轉貼] 為何我們要從Node.JS遷移到Ruby on Rails

原文

聲明:這篇文章絕不是一篇討論Node.JS和Ruby on Rails孰優孰略的檄文。它描述的只是我們做決策過程中的一些思考、決策背後的原因。兩種框架都非常優秀,都出色的完成了它們的設計初衷,這也是為什麼我們部分的模塊仍然運行在Node.JS上的原因。

我是Node.JS的大粉絲,認為這是一項讓人非常興奮的技術,相信它會變的越來越流行。我對這項技術非常的欣賞——儘管我們最近把Targeter App從Node.JS遷移到了Ruby on Rails。
我們當時使用Node.JS開發它的原因很簡單。我有一個程序包,能很快的將我們的應用弄上線(我們花了54小時做這個事情),相比起Ruby,我更常使用的是JavaScript。因為我們的技術架構牽涉到MongoDB,我的這些特長只有在Node.JS環境裡才會有意義。然而,隨著應用規模的增長,我認識到,選擇Node.JS來實現這個應用是個錯誤的選擇。下面讓我來概述一下其中的原因。

Node.JS很適合做那些有大量短生命期請求的應用。對於傳統的CRUD應用,它也很好,但不是非常的理想。在PHP,Ruby,Python語言裡都有很成熟、優化的很好的框架來處理這種應用。Node.JS裡的所有東西都異步執行的理念對於CRUD應用來說沒有任何效果。其它語言裡的流行的框架能提供非常好的緩存技術,你所有的需求都能滿足,包括異步執行。
Node.JS是一種非常年輕的技術框架,它的周邊程序庫都不是很成熟。我說這些並沒有任何對那些代碼捐贈者冒犯的意思,他們很優秀,開發出來很多優秀的程序庫。然而,大部分程序庫需要改進,而Node.JS的這種快速成長的環境意味著每一版升級中都帶有大量的變化;當你使用一種前沿技術時,你十分有必要盡快的緊跟最新的版本。這給創業型的企業帶來了很多的麻煩。

另外一個原因是關於測試。Node.JS裡的測試框架還不錯,但跟Django或RoR平台上的相比還是差一些。對於一個每天都有大量的代碼提交、並且在一兩天內就要發佈的應用來說,程序不能出問題是至關重要的,否則你為此辛苦的努力變得得不償失。沒有人願意花一天的時間改一些弱智的bug。 最後一點,我們需要的是一種能緩存一切的東西,並且要盡快的實現。儘管我們的應用在增長,每秒鐘有上萬次的hits,但絕不會出現很大量的訪問請求;這不是一個聊天程序!主程序最多時也就達到1000RPS,這樣的負載對於Ruby on Rails和Nginx來說算不了什麼。

如果你現在還在讀這篇文章,那你已經看到了我所有要說的了,你也許非常堅持的想知道我們的應用什麼地方還在使用Node.JS。是這樣的,我們的應用由兩部分組成。一是界面,用戶看到的這部分,二是負責報表管理的部分,以及做日誌的功能。後者是Node.JS的一個最佳使用場景,存在有大量的短週期的請求。這部分的動作需要盡快的執行完成,甚至要在我們的數據推送還沒有完成之前。這很重要,當請求執行還未結束,瀏覽器繼續等待響應結束,這會影響用戶使用體驗。Node.JS的異步特性救了我們。數據要麼被存入數據庫,要麼被處理掉,當請求一旦執行完成,瀏覽器就可以開始做其它重要的事情了。

[轉貼] C# Delegate/event 的簡單理解

看這篇前可以先看這篇文章瞭解Delegate的語法與使用方式
本篇原文=================
簡單來說就是增加一層間接層來實現晚綁定,從而獲得更為鬆散的耦合

為什麼要有Delegate? 
假設有兩個不同類實現的對象A和B,A和B分別有方法a和b。現在需要a方法以一觸發,便執行對象B的方法b。 不使用delegate,我們可以在設計A類的時候在將B類作為參數傳入a方法,這樣可以實現我們的要求。但是這樣做有明顯的缺陷:   
    1. 我們需要事先知道B類。   
    2. 如果還存在C類、D類等的方法需要一併執行,這樣必須重新修改A類的代碼。 如果有了  Delegate,我們可以事先設計一個函數指針,這樣當對象A執行a方法的時候,就可以根據該指針指向的,符合該函數指針法則的方法進行執行。這樣,我們就不需要在設計A類的時候知道B類,只需要知道B的b方法的參數和返回值就可以了。如果還有C類、D類方法需要一併執行,也可以添加到該指針指向的數據結構中。


示例代碼分析:
代碼
    class Program
{
static void Main(string[] args)
{
A a
= new A();
B b
= new B(a);
a.Method_A();
}
}


class A
{
public delegate void Delegate_A();
public event Delegate_A da;
public void Method_A()
{
da();
}
}

class B
{
public B(A a)
{
a.da
+= new A.Delegate_A(Method_B);
}
public void Method_B()
{
Console.WriteLine(
"B's method!");
}
}
1. 先看Class A中   
public delegate void Delegate_A(); 這個語句規定了函數指針指向的函數應該符合的規範:沒有返回值,沒有參數傳入。
2. 接著實例化了一個 Delegate_A的對象da,這裡使用event關鍵字表明它是一個事件。 
3. 再看Class B的的構造函數,可見B構造時,往對象a的事件中傳入了Method_B的函數地址(a.da += new A.Delegate_A(Method_B)
4. 在Main函數中執行A對象的Method_A時,觸發了事件da,而da指向了B的方法。這樣即便A對象對B對象一無所知,也同樣能執行B的方法Method_B。這就是Delegate的好處。

Event有什麼用:
實際上在上例中將Event關鍵字刪除程序同樣能執行。之所以要加上Event,是因為在程序編譯時,加上Event以後該Delegate的對象會成為該類的私有成員,這樣外界只能通過 += 和 -=來對Delegate的對象da進行操作,而不能直接進行賦值。

2010年9月28日 星期二

Java 物件清除與 finalize() 函式使用

垃圾回收器(garbage collection,簡稱GC)回收不再被使用的物件的記憶體空間
這點是自  C\C++ 以來,高階程式語言很大的改變之一

使用 C\C++ 語言撰寫時,memory leak 是最常發生的嚴重錯誤之一
如果使用完的物件沒有被清除,記憶體空間的資源也無法被釋放
Java的垃圾回收器能回收不再被使用的記憶體空間
但是有些重點必須要注意,垃圾回收器只能回收經由new產生的物件
被占用的特殊記憶體,例如從外部的其他語言獲得的物件,清除方式就不能透過GC進行
針對這類的物件,Java提供了 filanlize() method來因應

class 檔中可以定義 finalize() method
當垃圾回收器要開始釋放被物件占用的記憶體時,會先呼叫 finalize()函式
接著在下一次的垃圾回收動作發生時才會回收該記憶體空間
所以一般常見 finalize() 的定義是在垃圾回收時先執行某些重要的清理工作

有個可能比較容易引起誤會的地方是 finalize() 並不是C++的 destructor
差異點在於C++物件一定會被摧毀,所以destrucor絕對會被喚起
但是Java物件卻不一定會被GC回收
GC的執行條件是記憶體接近用完
假如直到程式執行結束都沒有啟動GC,記憶體空間會在程式終止時一次歸還
GC的執行會給程式帶來額外的負擔,能不執行的話自然是最好
因此 finalize() 並不一定會被呼叫
如果有一定要執行的清除動作,必須自行撰寫函式處理
通常影像處理類的任務比較有這類需求
finalize()真正適合使用的時機就只有要清除 non-Java 的物件

2010年9月3日 星期五

程式物件與記憶體的配置方式

物件在配置時能被儲存在五個地方:
1.暫存器(Registers)  2.堆疊(Stack)  3.堆積(Heap)
4.常數暫存空間(Constant storage)   5.Non-RAM儲存空間

以下對這五種方式做說明


1.暫存器(Registers):
   暫存器內建於CPU內,所以是速度最快的儲存位置
   但因為數量有限,所以這裡的物件配置都由編譯器來決定
   自由度比較高的C或C++也僅能向編譯器建議暫存器的配置

2.堆疊(Stack):
   堆疊的位置位於一般的RAM裡,處理器經由其指標(stack pointer)提供直接支援來運作
   當程式配置一塊記憶體時,stack指標會往後移動;釋放記憶體則會讓指標往前移回
   堆疊很快也很有效率,速度僅次於暫存器

   因為Java的編譯器必須自行建立移動stack pointer的程式碼
   所以必須要完全掌握存在stack裡的資料的實際大小與存活時間
   因為這個限制,儘管物件的reference可以儲存於其上,但一般的Java物件並不能如此

3.堆積(Heap):
   Heap是通用性質的記憶體儲存空間,也位在RAM裡
   與stack不同的是,它不需要事先知道必須配置於其上的物件大小與存活時間
   所以它有了相當的彈性

   Heap也是Java放置一般Java物件的地方
   每當在程式中執行 new 指令時,就會在heap上配置
   當然有彈性的另一面是配置的時間較stack來的長

4.常數暫存空間(Constant storage):
   因為常數值不會改變,所以有時候會被放在ROM(read-only memory)或內嵌式系統中
   內嵌式系統的例子舉Java的string pool,它是特殊的儲存空間

5.Non-RAM儲存空間:
   如果資料可以獨立於程式外,這類物件通常被儲存於磁碟裡
   這一類儲存空間的特色是,可以將物件轉換為可儲存於其他媒介的形式
   必要時也可以還原成儲存於RAM中的一般物件


我們稱呼在撰寫程式過程中常用到的類別為基本型別(primitive types)
要特別的處理它是因為透過 new 來配置這種極小、簡單的變數於heap上,效率不佳
Java採取C/C++的方式來處理,也就是不用 new 來配置其空間
這產生了"automatic"變數(不再使用reference型態)
這類變數直接存放資料值,並配置在stack上以取得較佳的效率