顯示具有 Programming 標籤的文章。 顯示所有文章
顯示具有 Programming 標籤的文章。 顯示所有文章

2016年1月21日 星期四

樣板類, 樣板函數


C++類模板詳解

如果說類是對象的抽象,對象是類的實例,則類模板是類的抽象,類是類模板的實例

C++函數模板

2015年2月15日 星期日

[轉] stack vs heap:執行時期儲存兩大要角

現代電腦系統大多依照Von Neumann Architecture設計而成,其中一特色stored programming乃指『程式執行一定要將欲執行的指令跟資料放入記憶體方可執行』,由此可知執行過程中記憶體所佔的地位厥偉之處。但許多工程師卻搞不清楚記憶體中的stack跟heap space到底有何居別,下面簡單針對兩者加以論述,希望對讀者有所幫助~

三分天下。程式執行過程中其實主要分成三大區塊:globalstackheap三塊。其中global區塊最最易理解,主要存放全域變數或宣告為static的靜態變數在此就不多做贅述;另外兩個區塊分別為stackheap這兩者往往混淆不清,尤其在java中有時候會出現stack overflowheap overflow到底兩者差異在哪,若工程師連這都不清楚那以後怎麼去調整JVM中的heap memory spacestack memory space的大小呢?

貼心的系統全自動化管理區塊:Stack Memory Space在記憶體中不外乎就是要存放變數、函式相關資訊等資料,使運作過程可以順利取得所需的變數或函式所在地。要讓系統可以全自動化管理,代表需可被預期此變數或函數資訊的生命週期,一旦完全可預測代表可以安心的交由系統管理,這些資訊也將在執行過程中被存放在stack空間。Stack中常見的存放資訊如下:區域變數(local variable)、函式參數(function/method parameter)、函數的返回位址(function/method return address)等資訊。為何上述資訊會放於stack之中,簡單來說:
void method1() {
  int x = 100;
}
上述的int x = 100,系統會在stack中找一個區塊給x,另外裡面的內容為100。然而,x會被放入stack主要是因為在編譯時期系統已經可以預知x從何時開始配置跟何時結束回收(當然就是看所屬block結束就跟著回收),由於配置跟回收的規則明確,當然就往stack擺囉。
在舉一例子:
void method2() {
  method1();
}
上述當呼叫method1()時,系統會先把method2的返回位址存到stack當中,為何是存放在stack呢,因為函式的呼叫有後進先出的概念,當method1()被呼叫而開始執行,待結束時必定會查找該返回何處,故最後一定會讀取函式的返回位址,既然如此明確而有條理,當然也是往stack放!

可預測性外加後進先出的生存模式,令stack無疑是最佳的存放策略。由於程式語言中變數跟函式的生命週期皆為後進先出的概念,也就是越晚產生的會越先被回收或銷毀。正因如此只要是可預測性的相關資訊都是往stack存放。此外,由於stack中的資料之存活週期規律故由系統自行產生與回收其空間即可,就不勞工程師們費心啦!

天啊!程式中竟然有不可預測其存活時間的資料存在。在程式中,有部分的需求總是在執行中依據實際情況才會動態增減,這些資訊是難以被預測哪時候開始有?量有多少?何時該回收?這些不可預測的因素造成上述的stack區塊不適合運用於此。當資訊為動態配置產生,系統會存放在另外一塊空間,稱之為『Heap(注意這裡的Heap跟資料結構中的Heap不相關,可別會錯意!)Heap的區塊專收執行期間動態產生的資料,由於為動態產生故結束點無法由系統來掌握,故需使用者自行回收空間。在C++Java中利用new語法產生的就是動態配置的物件,需存放於heap中。

奇怪跑越久記憶體用越多的怪現象。許多時候執行的程式都沒有改變,但卻常出現隨時間執行越久程式所耗用的空間將越多,最後造成out of memory。工程師也不知為何如此,就是定期在out of memory之前restart程式即可。這中現象層出不窮,一般大多是因為工程師沒有正確將記憶體回收所導致。Heap中的資料如果沒有正常的回收,將會逐步成長到將記憶體消耗殆盡,下次發生上述問題的實後,切記自己檢查一下heap空間的資料有無正常回收。論述到此有些讀者可能會覺得納悶:為何在寫Java都不需要注意回收空間的問題?~答案是因為Java中會採用Garbage Collection(垃圾回收)的機制自動檢查Heap中哪些資料已經沒有被使用,當確認資料已經沒有使用會自動將空間回收,如此工程師就專注撰寫程式即可,不用擔心記憶體回收不當等問題。

The conclusion is…當產生stack overflow一般是因為過多的函式呼叫(例如:遞迴太深)、或區域變數使用太多,此時請試著將stack size調大一點,另外檢查看看函式的呼叫跟變數的使用量。反之,當發生heap overflow請檢查是否都有正確將heap space的資料回收,另外採行的動態配置是否合理,不要過渡濫用而new出無謂的空間,若真的是程式過於複雜造成,請將heap size調大一些。

2015年1月22日 星期四

[轉] Thread-Safe的理解與分析


何謂thread-safe? 這個問題我看過許多論壇都有討論過,都總讓人覺得不很滿意。在此,筆者想要用更logical的方式來把議題說清楚。首先,我們要了解它的定義! 定義若都不明白就難以判別安不安全了!

thread是什麼呢? 可能也有人不太了解。就從這裡開始… 當cpu處理一段(區塊)的程式碼時,從開始的第一行程式碼來跑就算是thead的開始,直到區塊的最後一行程式結束,就算是thread的結束。所謂的「主thread」,在dos (或dos like)程式裡,就是指main( )這個函式的開始到結束的一個thread。而主thread以外的一個子thread則是指程式人員自行在主thread裡再定義一個「程式區塊」,並請cpu同步的去執行那個區塊。

由上面的thread的定義來看,我們可以進而推論所謂的multithread這個字其實是有三種情形的,哪三種呢?
1/ 相同的一個程式區塊,請cpu"分出多身"來執行它! (一段code被new或說create成數個thread)
2/ 不同的程式區塊,請cpu各別分時的去執行每個區塊 (幾段code個別被new或說create成個別的thread)
3/ 混合以上二種情形

所以,當我們在討論多緒/多線程(multithread)的問題時,我們要先去了解問題是上面三類的哪一類。若是第一類,那就要注意若有使用全域變數,那就極有可能是不安全的。若是第二類,那也要考慮是否不同的thread code是否有共用某同一個全域變數。

我們再把「程式區塊」給解析一下。它可以是一個function,或是一個class(裡面有成員函式),或是上述二者的混合體。因為function或是class都會使用到變數,於是多緒時變數的共享就成了一個很大的問題。這就是為什麼會有thread-safe的這個議題了!

到此為止,我們可以想像若一個程序(process / 主thread)裡,有大於等於一個子thread在跑,就會產生thread-safe的問題。以最簡單的情形來說,mainthread+user自定的一個thread。假設Button元件按下後會刪除memo元件裡某個String,而同時間的一個thread則會取用memo裡的String。假想當thread在取用memo中某String做分析的時候,user不知情的按下button,那麼thread是否有可能會取到不正確的String?  若不經過特別的安全設定,肯定「取用」時時而會出問題!

因此,到底安全或不安全的根源就在那執行的區塊程式碼上了(以上例就是取用的過程)。我要補充一個重要的事,其實每一個thread都有自己的一個專屬stack,所以auto級(local)的變數是不會有共享的問題,因為它們都是存放在thread各自的記憶體空間內,彼此不甘擾。也就是說,若你的程式區塊中,所有使用到的變數都是local(auto)變數,那你這個區塊,或說這個thread,就算是thread-safe !! 相反的,若你的區塊中有用到全域的變數,那麼你的這個thread就有可能是不安全。請注意看,是有可能,並非一定是。

如何把含有全域變數的thread分析出安全或不安全? 其實這是很直覺的。比如說某全域變數int g被數個thread使用到,但每個thread裡只引用到一次,那算安全嗎?  答案是…還算安全(註:極度的嚴僅來說不100%,但98%不為過)。因為單一基本變數的一次存取cpu處理極快,不太會有衝突。又,若是某全域結構體變數 struct tag 被引用存取到一次,那算安全嗎?  答案是…不太安全,因為一個結構體的存取可能涉及到多個微指令。進一步的用上面的例子來說,若int g被「程式區塊」(thread code)引用到2次(含)以上,那就肯定不安全了。比如說第一次是把g設成k,第二次把g再設給某變數i,若thread1正在處理第二次引用,而thread2在處理第一次引用。那thread1可能會得到意外的答案!(g值非它第一次設成的k值,而是變成thread2裡的k值)

如何讓不安全的thread變成安全的?? 其實主要的方法有二類。


1/ 存取共用全域變數時加上lock的機制,而這lock的機制在windows上可以使用mutex、criticalsection、event、semaphore等物件配合WaitForSingleObject或WaitForMultipleObject二個api來控制。其原理就是當某thread在取用資料時,其他的通通都停止並等待,直到該thread取用完。


2/ 把存取全域變數的動作通通交給某一個thread全全處理。比如說你有thread1,thread2,thread3。當要存取全域的struct變數時,thread2就把工作交給thread1做,然後自己停住等thread1完成,thread3也把工作交給thread1做,並等待…  。這原理應該容易理解,因為不安全的工作全都交給"單一"的thread來處理時,那份工作就安全了呀! 當然,這也不禁讓我們覺得,這樣效能非常的差!通通都在等thread1。  不過還好的是,若少少的用還ok,若許多的工作都交給某一thread順序來處理,那就真的沒意義了! 而這種方式其實就是bcb或delphi裡的synchronize這個方法的處理方式! (請看最下面2011年我的補充說明)

 

後註: class 其實也像是一種function,所以class本身也是存在著安全與否的問題。倘若該class裡的menthod有用到全域的變數時,該class也算是不安全的! 而許多的VCL就是這類的class!

註2: 若什麼是auto變數、stack都還不清楚,請先自行google或查程式語言的書,這是很基本且重要但易被忽略的章節。




2014年4月28日 星期一

COM 元件物件模型

元件物件模型[編輯]

元件物件模型,也稱元件物件模型,英文為Component Object Model,縮寫COM,是微軟對於網頁伺服
與客戶端、增益集與Office系列軟體之間互動的一項軟體元件技術。
自動化是指應用程式的COM化來讓外部可以呼叫它們的功能,以及外部呼叫這些功能的方法。自動化的典
型應用是微軟的很多軟體中都支援的巨集。由於自動化普遍使用OLE的標準介面來實作,所以這個技術有
時也稱OLE自動化。

歷史[編輯]

Windows作業系統提供了三種應用程式間的通訊機制:剪貼簿(clipboard)、DDE與OLE。OLE原名是物
件連結與嵌入(Object Linking and Embedding),OLE可說是DDE的改良版,OLE 1.0版提供複合文件
(compound document)處理。但過於複雜,Brockschmidt, Kraig「Inside OLE」一書中提到,必須經過
六個月的心靈混沌期,才能了解OLE是什麼。因此OLE 2.0後,微軟提出COM架構。所有OLE元件皆是
COM繼承而來的,這些技術包含OLE Document和OLE Controls、Drag and Drop等。

概要[編輯]

COM是微軟自1993年便提出的元件式軟體平台,用來做行程間通訊(Inter-process communication, IPC)
以及當作元件式軟體開發的平台。COM提供跟程式語言無關的方法實作一個軟體物件,因此可以在其他環
境中執行。COM要求軟體元件必須遵照一個共同的介面,該介面與實作無關,因此可以隱藏實作內容,並
且被其他物件在不知道其內部實作的情形下正確的使用。
COM並被實作於多個平台之上,並不限於Windows作業系統之上。但還是只有Windows最常使用COM,且
某些功能已被目前的.NET平台取代。

COM元件類型[編輯]

COM是基於元件物件方式概念來設計的,在基礎中,至少要讓每個元件都可以支援二個功能:
  • 查詢元件中有哪些介面
  • 讓元件做自我生命管理,此概念的實踐即為參照計數(Reference Counting)
這二個功能即為COM的根:IUnknown介面所提供的IUnknown::QueryInterface()
IUnknown::AddRef()IUnknown::Release()三個方法的由來。所有的COM元件都要實作IUnknown
表示每個COM元件都有相同的能力。
只由COM衍生實作出來的元件,稱為純COM元件
但在Windows持續發展時,Visual Basic 4.0開始支援OCX,也就是OLE Custom Control,這讓微軟開始思
考要如何讓COM元件可以跨語言支援,在這樣的要求下,必須要提供一個一致的介面,以及提供一組可以
呼叫介面內方法的能力,由於純COM元件只能夠支援C/C++ 的直接存取,為了要達到跨語言的能力,在
COM中必須要支援在外部呼叫內部方法的機能,這個機能造就了Invoke()方法,另外為了跨語言的支援,
COM應該要提供簡單的元件存取識別方式,這也就是會有GetIDsOfNames()的原因,將這些方法組合起
來,定義出的必要介面,稱為IDispatch介面,所有實作此介面的,都可以支援跨語言的支援。
微軟將實作此介面的元件都稱為自動化(Automation) 元件。

相關技術[編輯]

COM曾是Windows平台下主要的軟體開發平台,並且影響至其他許多相關軟體技術。

COM+[編輯]

COM+ 是微軟Windows 2000中,Microsoft Transaction Server的強化實作版本,除了提供基本的元件交易
支援外,還提供了鬆散藕合式事件(loosely-coupled events)與物件共用池(object pooling)等應用程式
伺服器的能力,成為Windows 2000開始在微軟平台上主要的應用程式伺服器平台,目前.NET Framework
也提供了System.EnterpriseServices命名空間以支援COM+。

Distributed COM[編輯]

Distributed COM是可以在網路上通訊的COM元件,依據RPC(Remote Procedure Call)的規範來發展的,
它將COM元件的能力擴及到網路上,但是因為網路安全以及防火牆的問題,讓裝置內容OM無法廣泛的流行。

2013年11月6日 星期三

bss,data,text,rodata,heap,stack,常量段

bss段:
BSS段(bsssegment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域BSS是英文BlockStarted by Symbol的簡稱。BSS段屬於靜態內存分配。
data段:
數據段(datasegment)通常是指用來存放程序中已初始化的全局變量的一塊內存區域。數據段屬於靜態內存分配。
text段:
代碼段(codesegment/textsegment)通常是指用來存放程序執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經確定,並且內存區域通常屬於只讀,某些架構也允許代碼段為可寫,即允許修改程序。在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量等。
rodata
存放C中的字符串和#define定義的常量
heap堆:
堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)
stack棧:
是用戶存放程序臨時創建的局部變量,也就是說我們函數括弧「{}」中定義的變量(但不包括static聲明的變量,static意味著在數據段中存放變量)。除此以外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束後,函數的返回值也會被存放回棧中。由於棧的先進先出特點,所以棧特別方便用來保存/恢復調用現場。從這個意義上講,我們可以把堆棧看成一個寄存、交換臨時數據的內存區。

常量段:
常量段一般包含編譯器產生的數據(與只讀段包含用戶定義的只讀數據不同)。比如說由一個語句a=2+3編譯器把2+3編譯期就算出5,存成常量5在常量段中

一般情況下,一個程序本質上都是由 bss段、data段、text段三個組成的——本概念是當前的計算機程序設計中是很重要的一個基本概念。而且在嵌入式系統的設計中也非常重要,牽涉到嵌入式系統運行時的內存大小分配,存儲單元佔用空間大小的問題。
在採用段式內存管理的架構中(比如intel的80x86系統),bss段(Block Started by Symbol segment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域,一般在初始化時bss 段部分將會清零(bss段屬於靜態內存分配,即程序一開始就將其清零了)。
比如,在C語言程序編譯完成之後,已初始化的全局變量保存在.data 段中,未初始化的全局變量保存在.bss 段中。   
l          text和data段都在可執行文件中(在嵌入式系統裡一般是固化在鏡像文件中),由系統從可執行文件中加載;
l          而bss段不在可執行文件中,由系統初始化

編譯兩個小程序如下:
程序1:
int ar[30000];
void main()
{
    ......
}

程序2:
int ar[300000] =  {1, 2, 3, 4, 5, 6 };
void main()
{
    ......
}
    發現程序2編譯之後所得的.exe文件比程序1的要大得多。 為什麼?
區別很明顯,一個位於.bss段,而另一個位於.data段,兩者的區別在於
l          全局的未初始化變量存在於.bss段中,具體體現為一個佔位符;全局的已初始化變量存於.data段中;
l          而函數內的自動變量都在棧上分配空間。
l          .bss是不佔用.exe文件空間的,其內容由操作系統初始化(清零);
l          而.data卻需要佔用,其內容由程序初始化,因此造成了上述情況。

注意:
l          bss段(未手動初始化的數據)並不給該段的數據分配空間,只是記錄數據所需空間的大小。
l          data(已手動初始化的數據)段則為數據分配空間,數據保存在目標文件中。
l          DATA段包含經過初始化的全局變量以及它們的值。
l          BSS段的大小從可執行文件中得到,然後鏈接器得到這個大小的內存塊,緊跟在數據段後面。當這個內存區進入程序的地址空間後全部清零。包含DATA和BSS段的整個區段此時通常稱為數據區。

Program / Process / Thread 的差別

Program:放在二次儲存裝置中,尚沒有被Load到記憶體的一堆Code
         稱之為「程式」。  (也就是還是死的)


Process:已經被Load到記憶體中,任何一行Code隨時會被CPU執行,且其宣告的在記憶體
         的變數的值會隨著需求而不斷變動。
         稱之為「程序」。 (也就是活的Program) => 恐龍本第三章
         一個多工作業系統(Multitasking Operating System)可以同時運行多個Process
         然而一個CPU一次只能做一件事情,但CPU的數量永遠少於運行中的Process數,
         因此每個Process使用的時間需要被排程(Scheduling) => 恐龍本第五章
         又每個Process間在記憶體中,如果擺放的方式不當,就會在記憶體中產生很多
         沒辦法用到的碎片,因此MemoryManagement是一個問題 => 恐龍本第八章
         另外,每個Process所需要的記憶體總合,也可能大於實體記憶體,因此需要另
         外用二次儲存裝置充當虛擬記憶體(Virtual Memory),但是二次儲存裝置的速
         度肯定很慢,因此如何做到對虛擬記憶體最小的依賴,盡量避免Page Fault(電
         腦在主記憶體中找不到資料,而要去二次記憶體找,就稱為Page Fault)
         防止Thrashing的發生(因為Virtual Memory演算法不當,造成幾乎每次存取都要
         依賴二次記憶體,就是Thrashing),以達到效能最佳化,也是個學問 => 第九章




Thread :在同一個Process底下,有許多自己的分身,就是Thread,中文又翻成執行緒。
         以往一個Process一次只能做一件事情,因此要一面輸入文字,一面計算字數,
         這種事情是不可能的。但是有了Thread之後,可以在同一個Process底下,讓輸
         入文字是一個Thread,計算文字又是另外一個Thread,對CPU來說兩個都是類似
         一個Process,因此兩個可以同時做。
         又一個Process底下有數個Thread,而一個Process的Global Variable可以讓
         它的所有Thread共享,也就是所有Thread都可以存取同一個Process的Global
         Variable。而每個Thread自己也有自己的專屬Variable。 => 恐龍本第四章
         但是,如果有兩個Thread要存取同一個Global Variable,有可能發生問題,
         也就是說可能會存取到錯的值(例如兩個Thread同時要對一個Variable做加減,
         最後那個答案可能會是錯的),這就是Synchronization問題 =>恐龍本第六章
         又,每一個Thread之間可能會互搶資源,而造成死結(Deadlock),只要以下四
         個條件都滿足就有死結。(1)這個資源不能同時給兩個人用 (2)有一個人拿了一
         個資源,又想拿別人的資源 (3)如果一個人占了茅坑不拉屎,占用資源很久,仍
         不能趕他走 (4)A等B,B等C,C等D,D又等A 等成一圈。 要解決這種狀況有
         Avoid(預防) 或 避免(Prevent)兩種方式,破除以上四種其中一種即可。
         => 恐龍本第七章

2013年9月24日 星期二

stack vs heap:執行時期儲存兩大要角


現代電腦系統大多依照Von Neumann Architecture設計而成,其中一特色stored programming乃指『程式執行一定要將欲執行的指令跟資料放入記憶體方可執行』,由此可知執行過程中記憶體所佔的地位厥偉之處。但許多工程師卻搞不清楚記憶體中的stack跟heap space到底有何居別,下面簡單針對兩者加以論述,希望對讀者有所幫助~

三分天下。程式執行過程中其實主要分成三大區塊:global、stack、heap三塊。其中global區塊最最易理解,主要存放全域變數或宣告為static的靜態變數在此就不多做贅述;另外兩個區塊分別為stack跟heap這兩者往往混淆不清,尤其在java中有時候會出現stack overflow或heap overflow到底兩者差異在哪,若工程師連這都不清楚那以後怎麼去調整JVM中的heap memory space跟stack memory space的大小呢?


貼心的系統全自動化管理區塊:Stack Memory Space!在記憶體中不外乎就是要存放變數、函式相關資訊等資料,使運作過程可以順利取得所需的變數或函式所在地。要讓系統可以全自動化管理,代表需可被預期此變數或函數資訊的生命週期,一旦完全可預測代表可以安心的交由系統管理,這些資訊也將在執行過程中被存放在stack空間。Stack中常見的存放資訊如下:區域變數(local variable)、函式參數(function/method parameter)、函數的返回位址(function/method return address)等資訊。為何上述資訊會放於stack之中,簡單來說:

void method1() {

  int x = 100;

}

上述的int x = 100,系統會在stack中找一個區塊給x,另外裡面的內容為100。然而,x會被放入stack主要是因為在編譯時期系統已經可以預知x從何時開始配置跟何時結束回收(當然就是看所屬block結束就跟著回收),由於配置跟回收的規則明確,當然就往stack擺囉。

在舉一例子:

void method2() {

  method1();

}

上述當呼叫method1()時,系統會先把method2的返回位址存到stack當中,為何是存放在stack呢,因為函式的呼叫有後進先出的概念,當method1()被呼叫而開始執行,待結束時必定會查找該返回何處,故最後一定會讀取函式的返回位址,既然如此明確而有條理,當然也是往stack放!

可預測性外加後進先出的生存模式,令stack無疑是最佳的存放策略。由於程式語言中變數跟函式的生命週期皆為後進先出的概念,也就是越晚產生的會越先被回收或銷毀。正因如此只要是可預測性的相關資訊都是往stack存放。此外,由於stack中的資料之存活週期規律故由系統自行產生與回收其空間即可,就不勞工程師們費心啦!

天啊!程式中竟然有不可預測其存活時間的資料存在。在程式中,有部分的需求總是在執行中依據實際情況才會動態增減,這些資訊是難以被預測哪時候開始有?量有多少?何時該回收?…這些不可預測的因素造成上述的stack區塊不適合運用於此。當資訊為動態配置產生,系統會存放在另外一塊空間,稱之為『Heap』(注意這裡的Heap跟資料結構中的Heap不相關,可別會錯意!)。Heap的區塊專收執行期間動態產生的資料,由於為動態產生故結束點無法由系統來掌握,故需使用者自行回收空間。在C++或Java中利用new語法產生的就是動態配置的物件,需存放於heap中。

奇怪跑越久記憶體用越多的怪現象。許多時候執行的程式都沒有改變,但卻常出現隨時間執行越久程式所耗用的空間將越多,最後造成out of memory。工程師也不知為何如此,就是定期在out of memory之前restart程式即可。這中現象層出不窮,一般大多是因為工程師沒有正確將記憶體回收所導致。Heap中的資料如果沒有正常的回收,將會逐步成長到將記憶體消耗殆盡,下次發生上述問題的實後,切記自己檢查一下heap空間的資料有無正常回收。論述到此有些讀者可能會覺得納悶:為何在寫Java都不需要注意回收空間的問題?~答案是因為Java中會採用Garbage Collection(垃圾回收)的機制自動檢查Heap中哪些資料已經沒有被使用,當確認資料已經沒有使用會自動將空間回收,如此工程師就專注撰寫程式即可,不用擔心記憶體回收不當等問題。

The conclusion is…。當產生stack overflow一般是因為過多的函式呼叫(例如:遞迴太深)、或區域變數使用太多,此時請試著將stack size調大一點,另外檢查看看函式的呼叫跟變數的使用量。反之,當發生heap overflow請檢查是否都有正確將heap space的資料回收,另外採行的動態配置是否合理,不要過渡濫用而new出無謂的空間,若真的是程式過於複雜造成,請將heap size調大一些。

程式的撰寫,其實跟製造業的加工廠很相似,套句郭董常說的:『魔鬼藏在細節中』~~

2013年9月22日 星期日

訊息傳遞(Message Passing)和函數呼叫(Function Call)的不同點


Procedure Language以函數作為寫作和執行的主體。函數由一群程式碼所組成,函數的開頭地址在Compile time或Link time就已經決定好了,而在Runtime呼叫函數時給予適當的參數。函數和記憶體之間沒有關聯性,而且函數A可以被任何其他函數呼叫,程式語言並沒有特別的規範。
Object-Oriented Language以Class作為寫作的主體,執行時主要由Object來紀錄程式狀態。由於物件導向程式語言將Object Variable和Object Method一起定義在Class內,再透過Encapsulation的機制規範存取Object Member的範圍,因此Object的Variable和Method就組成了一個完整的個體。
雖然Object Method寫起來就像Function,但執行Method內程式碼的機制和Function不同:
  1. Object Method定義物件接受到訊息時的反應, 也就是說執行Method時有一個隱形的參數, 意即這個物件(this)
  2. Function的實際地址在Compile或Link time就已經決定好了, 但對OO來說, Object必須在Runtime接受到訊息後, 才能決定實際要執行的Method。

Message Passing的語法

由於Java只能透過reference來存取物件,因此Message Passing的語法是reference.method
/**
 * 假設People有variable age,其值限定於0到130之間
 */
public class People {
    private int age;
    public People(int d) {
        this.age = d;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int d) {
        if (d >= 0 && d <= 130) { // 檢查年齡是否合法
            age = d;
        }
    }
    public void increaseAge() {
        if (age < 130) { // 檢查年齡是否合法
            this.age++;
        }
    }
    public static void main(String[] argv) {
        People e1, e2; // e1,e2 are references to Object People, not the Object themselves
        e1 = new People(3);
        e2 = new People(5);
        e1.setAge(30);
        e2.setAge(50);
        e1.increaseAge();
        e2.increaseAge();
    }
}
this這個keyword表示接收到此訊息的物件。由於設計時是定義Class, 而執行時則是Object接受訊息, Class只有一個, 但Object可以有很多個, 因此必須使用this表達現在接收到此訊息的物件。
由於Object Method具有隱形的物件參數, 因此Class Method不能去存取Object Member
public class ErrorCall {
    int data;  // object variable
    public void objectMethod() {
        data = 10;
    }
    public static void classMethod() {
        objectMethod(); // Compile Error
        data = 10; // Compile Error
    }
}