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年11月12日 星期一
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的異步特性救了我們。數據要麼被存入數據庫,要麼被處理掉,當請求一旦執行完成,瀏覽器就可以開始做其它重要的事情了。
聲明:這篇文章絕不是一篇討論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類方法需要一併執行,也可以添加到該指針指向的數據結構中。
示例代碼分析:
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進行操作,而不能直接進行賦值。
本篇原文=================
簡單來說就是增加一層間接層來實現晚綁定,從而獲得更為鬆散的耦合
為什麼要有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類方法需要一併執行,也可以添加到該指針指向的數據結構中。
示例代碼分析:
代碼
1. 先看Class A中 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!");
}
}
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 的物件
這點是自 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上以取得較佳的效率
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上以取得較佳的效率
訂閱:
文章 (Atom)