2.02.2016

Learning Java schronized

1. "synchronized" keyword on member method (lock on object)

The synchronized keyword on member method locks the object instance. If you have multiple threads using this object instance and calling the synchronized method, only one thread at a time can access within the synchronized block.
Note: The member methods without "synchronized" are not affected by the lock.
public class SynchronizeLab {
    public synchronized void doA() {
        System.out.println(String.format("%s is running doA at %d.", Thread.currentThread().getName(), System.currentTimeMillis()));
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void doB() {
        System.out.println(String.format("%s is running doB at %d.", Thread.currentThread().getName(), System.currentTimeMillis()));
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Now we have two threads triggering doA() and doB() at the same time. What will the output look like?
========== Note: The order is not guarantee ===
Thread-0 is running doA at 1454419074237.
Thread-1 is running doB at 1454419079239.
===============================================
As one thread is holding the lock, another thread has to wait until the first thread release the lock. Note: the synchronized method is the same as synchronized statements as below.
    public void doA() {
        synchronized(this) {
            .....
        }
    }
To improve the concurrency with fine-grained synchronization, the synchronized statements can also be used to lock on different object so that the lock on one object won't affect the other.

2. "synchronized" keyword on static method (lock on class)

The synchronized keyword on static method locks the class.
Note: The member methods with "synchronized" are not affected by the lock as they are object level lock.
public class SynchronizeLab {
    public synchronized static void doC() {
        System.out.println(String.format("%s is running doC at %d.", Thread.currentThread().getName(), System.currentTimeMillis()));
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Quiz: Assuming we have five threads triggering doA(), doB(), doC(), doD() and doE() at the same time, what will the output look like?

public class SynchronizeLab {
    private Object lockD = new Object();
    private Object lockE = new Object();

    /**
     * Thread holds the lock of the object
     */
    public synchronized void doA() {
        System.out.println(String.format("%s is running doA at %d.", Thread.currentThread().getName(), System.currentTimeMillis()));
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Thread holds the lock of the object
     */
    public synchronized void doB() {
        System.out.println(String.format("%s is running doB at %d.", Thread.currentThread().getName(), System.currentTimeMillis()));
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Thread holds the lock of the class
     */
    public synchronized static void doC() {
        System.out.println(String.format("%s is running doC at %d.", Thread.currentThread().getName(), System.currentTimeMillis()));
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * Thread holds the lock of the member variable
     */
    public void doD() {
        synchronized (lockD) {
            System.out.println(String.format("%s is running doD at %d.", Thread.currentThread().getName(), System.currentTimeMillis()));
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Thread holds the lock of the member variable
     */
    public void doE() {
        synchronized (lockE) {
            System.out.println(String.format("%s is running doE at %d.", Thread.currentThread().getName(), System.currentTimeMillis()));
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

========== Note: The order is not guarantee ===
Thread-2 is running doC at 1454595907992.
Thread-1 is running doB at 1454595907992.
Thread-3 is running doD at 1454595907993.
Thread-4 is running doE at 1454595907993.
Thread-0 is running doA at 1454595912995.
===============================================
Only doA and doB have exclusive lock on the same object.

6.27.2015

Javascript scope

Defining variables and functions:
var glob = 'glob'; // global variable
function printGlob() { // global function
    console.log(glob);
}
function Person(name, nat, gend) { // constructor
    this.name = name; // public variable
    nationality = nat; // global variable ("no var" will look up the scope chain   until it finds the variable or hits the global scope (at which point it will create it))
    var gender = gend; // local variable
    var privateMethod = function() {
        console.log(executing private method);
    }
    this.getName = function () { // privileged method that can access to private   method and local variable
        console.log(gender);
        privateMethod();
        return this.name;
    }

}
// public method (same as getName, but more memory efficient since all instances   share the same method)
Person.prototype.getName2 = function () { 
// can not access to private method and local variable
console.log(this.name);
    return this.name;
};
// static property
Person.town = "South Park";
How to use variables and functions:
// Access to global variable
nationality; // undefined since we haven't initialized it
// Create a new instance
var woody = new Person('Woody', 'TW', 'M');
// Access to global variable
nationality; // TW
// Access private property
woody.gender; // undefined
// Access privileged method
woody.getName();
// Access public property
woody.name; // Woody
// Access privileged method
woody.printPrivate(); 
// Access public method
woody.getName2(); 
// Access static property on an instance
woody.town; // undefined
// Access static property on the constructor object
Person.town; // "South Park"

12.19.2014

user process limit on Linix

最近在Linux遇到了一個ulimits (limits)問題,因為對這部份之前沒研究,infra team的人又在忙沒空看,只好自己上網找解,噴掉了我快2天的時間,想說跟大家分享一下!這個錯會在你執行指令時發生,如下:

[apprm@OTALOG logs]$ ls -lrt

-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: Resource temporarily unavailable

一開始google,很多篇都說是因為file descriptor用完了(也就是open file限制),查了後並沒有問題,後來發現其實是因為"max user processes"限制,下面來介紹一下面對這個問題可能的原因。

1. File descriptor用完了

一般來說,會出錯這個錯是因為file descriptor用完了,在Linux有分幾種不同的file descriptor限制
1.1 系統整體的file descriptor限制
[apprm@OTALOG ~]$ sysctl fs.file-nr
fs.file-nr = 2656 0 10240
這個指令會印出3個數字,第一個是目前使用的file descriptor數目,第二個是分配但未使用的數目,第三個則是系統最大的file descriptor。以上面來看,明顯問題不是在這。
1.2 特定user的file descriptor限制
[apprm@OTALOG ~]$ ulimit -Hn
8192
[apprm@OTALOG ~]$ ulimit -Sn
8192
[apprm@OTALOG ~]$ lsof -u apprm  | wc -l
4058
user的file descriptor限制分為soft limit和hard limit。soft limit一般而言不會是問題,因為process在執行時若有需要可自行突破上限;而hard limit則不行,除非有root權限才能突破hard limit限制。從執行的指令來看,hard limit設20480,soft limit設為8192,目前使用者用了4058,看來問題也不是在user limit。

若你在1.1或1.2的使用值非常接近或等於上限,恭喜你找到問題點!你要去修改相對應的檔案 針對1.1,要在/etc/sysctl.conf加上一行"fs.file-max = 10240",10240視情況而定(必需大於hard limit) 針對1.2,要在/etc/security/limits.conf 分別設定soft limit和hard limit * soft nofile 8192 * hard nofile 8192

2. 到達最大process(max user processes)限制

若問題不是出在file descriptor,那應該就是因為max user processes限制(預設為1024,root不受限制)。Linux並沒有thread,只有light weight processes(LWPs),基本上LWPs就跟其他作業系統的thread一樣,所以你可能只起一個application server instance,但為了performance,可能一開始就建立了200個thread的thread pool,在計算每個user process時,包含了user起的所有process以及LWPs,因此當server instance一多,很容易就達到預設上限
[apprm@OTALOG logs]$ ulimit -a | grep processes
max user processes              (-u) 1024
[apprm@OTALOG logs]$ ps -eLF | grep ^apprm | wc -l
1023
在第一個查詢我們得知目前的max user processes為1024,第二個查詢發現,apprm已經使用了1023個processes!Bingo!找到問題了!
修正方式是在要在/etc/security/limits.conf 加入下面設定 (2048可視需求調整) apprm - nproc 2048

12.14.2014

常用的grep指令

grep在Unix中是相當常用的指令,可以用來快速找到想要的檔案內容,下面我列出幾個我常用的指令給大家參考:


1. -v 排除特定字串

  • 如:grep Exception logfile.txt | grep -v ERROR

2. -c 計算特定字串出現的次數

  • 如:grep -c UNIX example.txt

3. -C 列印出特定字串前後文

  • 如:grep -C 6 Exception logfile.log (印出前後各6行)

4. egrep 使用extended的grep來做regular expression查詢

  • 如:egrep 'Error|Exception' logfile.txt (找出Error或Exception)

5. -i  不分大小寫

  • 如:grep -i Exception logfile.log

6. zgrep  在gz壓縮檔內的檔找特定字串

  • 如:zgrep -i Error *.gz

7. -l 列出有符合字串的檔名

  • 如:grep -l main *.java

8. --color 在搜尋結果中,highlight符合的字串

  • 如:grep Exception logfile.txt --color

12.07.2014

Java的十個物件導向設計原則

對於開發者而言,想必大家都知道design pattern的重要性;但這篇不是要講design pattern,我們要來看看物件導向程式設計的基本原則,其中包含了SOLID(Single responsibility, Open-closed, Liskov substitution, Interface segregation 和 Dependency inversion),這些基本原則可以幫助你寫出更簡潔、易維護、低耦合的系統。

不要自我重覆(DRY:Don't Repeat Yourself)

不要寫出重覆的程式(功能)!若有一部份的程式出現在不同地方,你可以考慮使用抽象化(Abstraction: 將相同的邏輯往parent搬)或委派(Delegation:將相同的任務委派給另一個物件,或另一個method完成),若有hard-code value重覆出現,則使用public final取代之,藉此可以提升維護性
這個原則很重要,但要小心不能濫用,這裡的程式並不是指程式碼,而是指功能。假設我們使用同樣的程式來驗證資料A以及資料B,雖然現在A及B有著相同格式,若我們使用委派,將相同的驗證抽成一個共用method,代表我們認為A和B的格式永遠不會變動,一旦未來其中一個格式有變動,改了驗證程式就會造成另一個資料驗證問題(若沒有Unit test特別容易發生)。因此要特別注意,這個原則適用在相同的功能而不是相似的程式


將變動封裝起來(Encapsulate Changes)

軟體開發中唯一不變的就是”變動”,變動可能來自於需求的改變、使用不同的工具等,要減少變動影響範圍,我們要將可能會變動的程式封裝起來。例如要發動車子,內部有很多零件必需協同和作才能完成,但用戶不需要知道這些細節,他們只需要將鑰匙轉動即可。由於封裝將細節隱藏起來,只提供主要介面供使用者驅動,若內部實作細節有任何異動,用戶端程式可以不用做任何異動。封裝簡單說就是將變數及method儘可能private化,至於要怎麼判斷程式碼是否有做到封裝,依我的經驗,若有段程式從物件A get不同參數值或呼叫A的不同method,很可能這段程式是屬於A的責任,你已經違反了封裝的概念。不要小看封裝,若你的實作散落各地,實作內容變動愈大,你所要修改的類別可能愈多

開放關閉設計原則(Open Close Design Principle)

有點難理解的原則,又開放又關閉的!其實指的是不同東西,開放指的是類別及方法要能夠擴充(加新功能或不同實作),關閉指的是關閉修改(refactoring例外)。理想上當你要加入新功能,應該引入新的類別、方法或變數,而不是去修改既有已經完成測試的程式,這可以避免修改造成的regression bug。
但要如何使程式能夠擴充呢?基本上我們可以運用多型(polymorphism),藉由相同介面(interface或super class),讓我們可以在擴充行為時不影響client端以及既有的程式

單一責任原則(Single Responsibility Principle)

責任指的是變動的理由,一個類別應該只有一個變動的理由例如一個類別負責編輯報表以及列印報表,這個類別就有二個變動的理由;一個是資料的異動,另一個是報表的格式異動。將二個不同責任綁在一起,除了會造成閱讀困難外,也會使其不好維護,畢竟我們不能保證修改其中一個功能時,對另一個功能不會造成任何影響。


相依性注入或反轉原則(Dependency Injection or Inversion Principle)

相信有使用Spring Framework的都對這個原則不漠生,一般我們要使用一個物件可能會直接new 一個instance,但DI告訴我們不要這樣,你應該動態注入相依的物件,這種方式可以帶來下面二種好處:
  1. 讓我們在撰寫測試時可以利用mock object,讓我們專注在該測的功能上
  2. 可藉由修改設定檔注入不同實作(Polymorphism),而不必修改程式。

傾向使用Composition多於繼承(Favor Composition over Inheritance)

使用繼承我們可以將共同的程式碼移至super class,使用composition我們則是將共同的程式碼移至composed class,在不考慮使用繼承是不是好的設計狀況下,二種差別在於彈性:使用繼承我們無法動態變動實作,使用composition,我們可以藉由polymorphism,讓不同實作有相同介面,藉由執行期的相依性注入抽換不同實作,任何時候我們都可以切換不同的實作且不用改程式。

介面分離原則(ISP:Interface Segregation Principle)

這個原則指的是client不應實作一個他不提供的功能介面會違反這個原則,大多是因為一個介面負責了超過一個功能(違反單一責任原則),而client只需要其中部份功能。在介面的設計上必須特別小心,因為一旦發佈了後,日後所做的任何修改都會使現有的實作無法運行。

Liskov 替換原則(Liskov Substitution Principle)

這個原則指的是子類別的實作必需可以替代父類別!也就是說父類別的實作必需在子類別也能運作LSP和單一責任原則以及介面分離原則有很強的關聯,若一個類別負責許多許責任,子類別一旦無法支援所有行為,就會違反LSP。要遵守LSP,則子類別必須是加強父類別的功能,不能減少。舉個教科書常見的錯誤例子來說:父類別Rectangle定義了長及寬變數,以及取得面積的方法,子類別Square繼承了父類別的長及寬變數以及行為,當我們以polymorphism的角度使用Square類別(Rectangle r = new Square())時,使用者認為他必需設定長及寬來計算面積,但當他設定長=10,寬=5並計算面積時,發現回傳是25(因為正方形四邊都一樣)而不是50!因為polymorphism開發時操作的是super class或interface,因此違反了這個原則可能帶來用戶錯誤的預期

針對介面而非實作(Programming for Interface not implementation)

這個原則其實就是要我們善用polymorphism,定義變數或方法變數或回傳變數時使用介面而非實作可以帶來彈性,當要替換不同實作時,這可以讓你減少異動

委派原則(Delegation Principle)

不要試著在一個類別中做所有的事,把責任委派給該負責的類別。例如hash code和equals method,要比較二個物件是否相同,我們並不會在client端寫程式去比,而是交由二個物件本身自己去比較,這樣的好處是可以減少重覆的程式碼,讓系統更好維護。

你可能對下面主題有興趣:
  1. [OO概念]封裝,繼承,多型
  2. [Java 概念]Interface and abstract class

12.06.2014

Garbage Collector的種類

前面(什麼是Garbage Collection)提到身為開發者,Java會自動回收記憶體,讓開發者可以專注在業務邏輯的處理,但在回收的過程中,會造成系統無法回應(取決於Major/Minor GC);因此Java提供了許多不同的Garbage Collector,讓我們可以依據系統的效能需求,選擇最適合的Garbage Collector。
如下圖所示,有七種Garbage Collector,分為上下二個區塊,上面為Young generation,下面為Old/Tenured generation,若二個Garbage Collector之間存在連線,代表可以搭配使用。

http://blog.csdn.net/java2000_wl/article/details/8030172

Serial Collector(-XX:+UseSerialGC)

屬於單一執行緒的copy collector,運作原理是將存活的物件從"from" space (ex.survivor 1)搬至"to" space(ex.survivor 2),並回收"from" space,他主要是設計給heap size需求很小的應用程式使用,由於屬於"Stop-the-world" (當他在執行時,其他任務都必需暫停) collector,所以並不適用於對response time要求高的系統,但相對地也因為單執行緒,讓他可以有最高的GC效率,較為適合用於不要求即時性(ex background job server)的系統。


ParNew Collector(-XX:+UseParNewGC)

Parallel copy collector其實就是multi-thread版的Serial Collector,其餘行為都跟Serial collector相同,效率則因為multi-thread較Serial Collector好。


Parallel Scavenge Collector(-XX:+UseParallelGC)

運作方式跟Parallel copy collector相同,但演算法有針對超過10GB heaps的multi-CPU環境做過最佳化。他的目標是盡可能地取得最大throughput[註1]同時降低GC造成的暫停。若你使用這個collector,則在old generation你可以使用原使的mark-sweep collector(Serial Old)。


CMS(-XX:+UseConcMarkSweepGC)

CMS不同於其他garbage collector,他不會進行heap space compaction(因為compaction的過程很耗時且會造成系統停頓),使他適用於高互動式的系統;但相對地,他也較容易造成記憶體碎片化(fragmentation);雖然CMS不會進行Full GC,但當出現promotion failed/concurrent mode failure時,此時會使用Mark Compact garbage collector (Serial Old GC)進行Full GC,CMS運作分為下面幾個階段:
Initial mark - 這個階段會找出application有直接(root)reference reference的活物件, stop-the-world 。
Concurrent mark – 這個階段會針對上階段結果,追蹤整個object graph,並標記活著的物件,整個過程會和application同時運行。
Concurrent pre clean – 這個階段是針對上一階段標記為活著的物件再做檢查,目的是為了減少Remark造成stop-the-world的時間,整個過程會和application同時運行。
Remark - 因為在進行mark階段,application是持續在運行,新的物件並不會被標記,此時application再度停止運行,並針對在Concurrent mark階段有變動的物件進行最終標記 , stop-the-world 。

Serial Old(-XX:+UseSerialGC)

Serial Old (Mark Sweep Compact:MSC) - 屬於stop-the-world collector,運作原理是先找出活著的物件做標記,之後清除未用的物件,最後再進行compaction。


Parallel Old(-XX:+UseParallelOldGC)

Parallel Scavenge的Old generation版本。


G1(-XX:+UseG1GC)

G1GC在Java7時加入,他的目標是在不犧牲throughput的狀況下,達成low latency。傳統GC會將heap分為3個區塊:young generation,old generation以及permanent generation,每個區塊都設定了固定大小;G1GC則是將heap分割成一塊塊相同大小的region,每個resion大約是1MB~32MB的連續記憶體空間,再將一個個region組成region sets,同一個region set扮演相同的角色(eden, survivor或old),因為region set沒固定大小,若服務中大部份的物件存活期都很短,則young generation會佔大部份;反之則tenured generation佔大部份,因此使用G1GC時,調整新世代大小的參數就不重要了,這種方式提供了記憶體使用上的更大彈性。

G1GC的GC過程跟CMS很類似,都是分階段標記後再回收,差別在於回收的方式。G1GC將記憶體分割為許多region,GC時並不會一次對所有region都進行回收,而是會依據允許的時間(預設為200ms),能回收多少region就做多少,藉此精準地控制停頓的時間,GC會先回收垃圾最多的region,以釋放最多空間,這也就是為什麼他叫Garbage First的原因。回收的過程採用驅離(evacuation)的方式,從一個或多個region將活著的物件搬至另一個region,藉此同時進行記憶體回收以及compaction,避免了heap破碎化的問題。



[註1]Throughput的定義是扣除GC花費的時間,系統可以使用的時間比;也就是[系統可用時間]/[總系統執行時間]。

12.02.2014

關於GC的JVM參數

了解GC相關的JVM參數對於在乎即時性的系統相當重要,例如高流量低latency的電子商務系統或線上交易系統,每一秒都相當重要。然而GC tuning需要不斷的trial-and-error以及profiling來找出最適合的設定,並沒有特定參數適合所有系統
在tuning的過程中,profiling可以幫助我們了解application會產生什麼物件,以及這些物件的平均生命週期。舉例來說,若你的系統大多數是生命週期很短的物件,加大Eden space會有效降低minor GC的次數;但也不能無限制的擴大,因為Young generation愈大,代表Tenured generation愈小,可能因此導致Tenured generation很快就滿了,必需頻繁地進行Full GC,降低整體系統的throughput[註1]。

接下來讓我們來看與GC相關的重要參數:


  1. Java heap size相關的JVM options (必要)
    • -Xms1024m  設定初始Java heap size (必要)理想的大小為使用量佔60%,這樣可以確保當忽然湧進大量使用者時,系統仍有一定的成長空間。建議Xms:Xmx比例為1:1或1:1.5,設為相等較為常見,以避免每次GC完後重新分配記憶體大小(CPU time)。
    • -Xmx1024m  設定最大Java heap size (必要)
    • -XX:NewRatio=3  設定Young跟Tenured generation的比例,一般建議設為3~4(譬如總heap為1024m,若設為3,則Young space為256m,Tenured space為768m)
    • -XX:NewSize=512m  設定default Young generation的大小,一般會設的跟max一樣,若你需要比NewRatio更精準的控制大小時才會使用
    • -XX:MaxNewSize=512m  設定max Young generation的大小
    • -Xss     設定Java thread stack size
  2. Perm Gen size相關的JVM options (可以解決java.lang.OutOfMemoryError:Perm Gen Space)
    • -XX:PermSize=256m  設定預設Perm space(建議和最大值相同)
    • -XX:MaxPermSize=256m  設定最大Perm space
  3. 列印GC log相關的JVM option (必要)
    •  -verbose:gc   Log garbage collector執行結果以及花了多少時間 (必要),開啟以利後續GC tuning
    • -XX:+PrintGCDetails   除了包含 -verbose:gc的資訊外,額外提供new generation大小以及GC花費的時間 (必要)
    • -XX:-PrintGCTimeStamps     印出GC執行時的時間點 (必要)
    • -XX:+PrintGCDateStamps    印出GC執行時的日期
    • -XX:+HeapDumpOnOutOfMemoryError   當OOM error發生時,自動產生heap dump (必要)
    • -XX:HeapDumpPath           將產生的heap dump檔案放至指定目錄 (必要)
  4. 指定Garbage collector的參數 
Classification Option Remarks
Serial GC -XX:+UseSerialGC
Parallel GC -XX:+UseParallelGC
-XX:ParallelGCThreads=value
Parallel Compacting GC -XX:+UseParallelOldGC
CMS GC -XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=value
-XX:+UseCMSInitiatingOccupancyOnly
‑XX:+CMSConcurrentMTEnabled
G1GC -XX:+UseG1GC available from Java 7 

[註1]Throughput的定義是扣除GC花費的時間,系統可以使用的時間比;也就是[系統可用時間]/[總系統執行時間]。

12.01.2014

什麼是Garbage Collection以及他在Java如何運作

JAVA並不像C或C++必需由開發者負責記憶體分配/回收的任務,JVM會依據設定,找出沒被參照的物件,自動進行記憶體空間回收,這使得讓開發者在開發時可以專注在業務邏輯上。雖然開發者可以不用管記憶體的分配/回收,但為了系統效能最佳化,我們還是有必要了解Garbage Collection(以下簡稱GC)是如何運作的,在開始之前,我們必須強調GC雖然可以自動回收沒被參照的物件,但無法解決程式設計不當所造成的memory leak。

首先來看GC的幾個重點

  1. 不論物件的scope(local variable/member variable),所有的物件都是儲存在Heap space!
  2. GC是JVM用來回收記憶體的機制,符合GC條件的物件會在GC過程中,釋放其佔用的記憶體空間(Heap space)
  3. GC讓開發者不用處理記憶體管理,在開發時可以專注在業務邏輯上
  4. Java透過一個叫Garbage Collector的daemon(背景)thread來進行GC
  5. 開發者無法強制執行GC,GC只會在JVM認為需要(根據Heap Size)時才會執行
  6. System.gc()和Runtime.gc()[註1]會送GC請求給JVM,但JVM並不一定會執行GC
  7. Heap space沒空間存放新建立的物作,則JVM會丟出OutOfMemoryError java.lang.OutOfMemoryError heap space
  8. Minor GC和Major/Full都是"Stop the World"事件,只是Minor GC時間非常短(幾百milli-seconds),使用者較不容易察覺;而Full GC時間相對上長很多,且heap size愈大時間愈久;因此應儘量避免或減少Full GC發生。


物件什麼時候會符回GC條件

物件若被JVM認為沒有在使用,即符合GC條件。判斷方式則是:JVM會從根物件開始走訪所有reference,把走到的做註記,當全部走完後,沒被註記的物件代表沒有人能夠取得,即符合GC條件。
一般而言,假設我們要讓物件A符合回收條件,可以通過下列方式達成:
  1. 將所有參照到A的變數設為null (ex. sample = null;)
  2. 在method中建立的物件A,一旦離開了該method,則所有local variable參照都會失效
  3. 將parent object reference設為null。parent object會參照到物件A,當parent object的reference消失時,則物件A也符合GC條件
  4. 若物件A只有被WeakHashMap參照到,則符合條件

Heap Generations

Heap可分為3個generations,分別稱為: Young/New generation, Tenured/Old generation和Permanent(Perm) generation,其中Young generation可以進一步分為3個區塊: Eden space, Survivor 1和Survivor 2。當物件一開始被產生時,他被放置在Eden space,當Eden space滿了,無法存放新物件時,JVM會啟動Minor GC,把存活的物件往Survivor 1或2移及以原本在Survivor 1或2的存活物件往另一個Survivor空間移,當JVM執行多次Minor GC後,會把符合條件的存活物件往Tenured generation移,這個過程我們稱為Minor GC。Tenured generation滿了時,JVM會執行GC,回收Tenured generation的空間,我們稱之為Major/Full GC
http://javahash.com/java-memory-model-structures/

Permanent generation儲存class和method相關的metadata以及interned String[註2]。至於perm space會不會進行GC,則是依JVM而定,Sun/Oracle實作的JVM會進行GC,要確認你用的JVM是否會進行GC,可以寫支小程式建立數百萬個字串看看是否會出現GC log或 OutofMemoryError。

物件的旅程

在了解Heap Generation後,我們來看看一個物件生命週期中,他在這些generation間的旅程(並不一定會走完全程,若期間符合GC條件,則提前結束)。
  1. JVM配置Eden記憶體空間給新建立的物件
  2. 當Minor GC時(Eden space無法分配記憶體空間給新建立的物件),物件從Eden被移到Survival space
  3. 當Minor GC時,物件從Survival space被移到另一個Survival space(根據設定,這可能會發生很多次)
  4. 當Minor GC時,物件從Survival space移(promote)至Tenured/Old generation
  5. Major GC(old generation無法分配空間給被promote的物件)

[註1] System.gc()和Runtime.gc()其實做一樣的事,System.gc()轉發request給Runtime.gc(),差別是System.gc()是class level而Runtime.gc()是instance level,System.gc()較為方便而已
[註2]All literal strings and string-valued constant expressions are interned.透過String.intern(),可以讓相同值的字串只存一份在記憶體,在JDK6,intern的字串存放於Permanent generation,在JDK7之後移至Young/Old generation
你可能對下面主題有興趣:
  1. 關於GC的JVM參數

2.24.2013

[Java 概念]Interface and abstract class

在上一篇介紹了OO的基本概念:封裝,繼承和多型,這篇要來介紹一下Java的介面和抽象類別,他們的差別是Java面試時常見的問題,下面來介紹一下他們的差別,以及使用時機。

介面和抽象類別的差別


  1. 介面本身並不包含實作,他只定義行為,實作介面的類別代表他有此行為。相對地,抽象類別本身可以包含抽象和實體方法,可以提供共同/預設的行為。
  2. Java class可以實作多個介面,但只能繼承一個抽象類別,介面能提供更多的多型支援。
  3. 要實作一個介面,你必需實作其所有行為,一旦行為一多,對於實作類別來說相當痛苦,因此較好的設計是一個介面最多提供1~2個行為,不要設計一個介面包含過多行為;而抽象類別則是一開始就提供了預設的實作。
在前文提到,多型本身可利用介面或繼承達成late-binding,以前的我會搞不太清楚何時用介面,何時用抽象類別(繼承),在這裡分享一下我的心得:

介面和抽象類別的使用時機


  1. 因為Java不支援多重繼承但可以實作多個介面,若你想要更多的多型支援,那就必需使用介面。
  2. IS-A關係,同型態的物件本身的行為相同時,就適合用繼承。至於super class本身需不需要Abstract,則是看情形,我認為大部份都是Abstract為多數,通常Abstract class會定義演算法,而將不同的部份定義為abstract method,由各子類別進行實作,這也就是Template Method pattern。
  3. 介面用於定義支援的行為,譬如:Runnable支援run(),Callable支援call(),當只需定義行為,而每個型態的物件本身的行為不同時,就適合用介面。

你可能對下面主題有興趣:
  1. [OO概念]封裝,繼承,多型
  2. Java的十個物件導向設計原則

2.23.2013

[OO概念]封裝,繼承,多型

最近發現最基本的問題,好像大家反而忽略了,想說來分享一下我對基本OO的封裝,繼承,多型見解。

封裝(Encapsulation):就是把不必要的資訊隱藏(Information Hiding)起來,只把必要的操作開放出去。譬如開車加速,我們只要知道踩油門就好,不需知道細節(內部零件間怎麼協同完成這件事),這樣的好處是物件間或模組間的藕合力(Coupling)低,若今天要完成一件事的細節變了,呼叫者可以完全不受影響,藕合力愈低,單元測試愈好寫啊...當你呼叫一個物件的get,就要小心也許你正在破壞他的封裝!

繼承(Inheritance):我們都知道就是父子關係(IS-A relationship),子類別會繼承父類的方法和屬性,繼承本身並沒有問題,但常會見到誤用的情形!就是開發者忘了IS-A的概念,為了reuse,造成了功能型的繼承!若要reuse且非IS-A關係,應用Composition來達成,把共同的部份移至composed class,把要做的事delegate另一個物件完成!

多型(Polymorphism):延申自繼承(Inheritance)或介面(Interface),指的就是不同型態的物件,定義相同的操作介面,由於被呼叫者(Callee)有著相同介面,呼叫者並不用指定特定型別,只需針對介面進行操作,實際執行的物件則在runtime決定,藉此增加程式碼的彈性。
你可能對下面主題有興趣:
  1. [Java 概念]Interface and abstract class
  2. Java的十個物件導向設計原則

7.30.2009

JNDI Resources

之前用Spring習慣把一些設定檔(如db的url/username/password)寫在config.xml裡
再透過Spring把這些properties load進去
現在沒有Spring要開始改用JNDI
J2EE application server有JNDI
Tomcat則是實作JNDI(Java Naming and Directory Interface) InitialContext給web app
要寫JNDI resources要在$CATALINA_HOME/conf/server.xml這個檔寫
詳細內容JNDI Resources HOW-TO

7.17.2009

activated object

this represents the activated object
ex. this.id , this.className += "highlight"

if (cellIsEmpty(this)) {
// User clicked on an empty cell
alert("Please click on a numbered tile.");
return;
}

7.16.2009

scroll images


function scrollImages() {
var coverBarDiv = document.getElementById("coverBar");
var images = coverBarDiv.getElementsByTagName("img");
for (var i = 0; i < images.length; i++) {
var left = images[i].style.left.substr(0, images[i].style.left.length - 2);
if (left <= -86) {
left = 532;
}
images[i].style.left = (left - 1) + "px";
}
}

ajax request object

If you are making two independent ajax request, you have to create two independent ajax request object.

function checkA(){
var request = createRequest();
}

function checkB(){
var request = createRequest();
}

You might think you have created two independent request object. In fact, you only create one request object. This might make your system prone to error.

the better way

function checkA(){
var requestForA = createRequest();
}

function checkB(){
var requestForB = createRequest();
}

By removing "var", we can make the request object become a global variable and make sure each request only coping with one ajax request.

7.13.2009

javascript trim

//trim both side of space
function trim(str){
return str.replace(/(^\s*)|(\s*$)/g, "");
}
//trim left side of space
function ltrim(str){
return str.replace(/(^\s*)/g,"");
}
//trim right side of space
function rtrim(str){
return str.replace(/(\s*$)/g,"");
}
function trimEnter(str){
return str.replace(/\n\r/gi,"");
}

6.05.2009

cookie

function writeCookie(name, value, days) {
  // By default, there is no expiration so the cookie is temporary
  var expires = "";

  // Specifying a number of days makes the cookie persistent
  if (days) {
    var date = new Date();
    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
    expires = "; expires=" + date.toGMTString();
  }

  // Set the cookie to the name, value, and expiration date
  document.cookie = name + "=" + value + expires + "; path=/";
}

function readCookie(name) {
  // Find the specified cookie and return its value
  var searchName = name + "=";
  var cookies = document.cookie.split(';');
  for(var i=0; i < cookies.length; i++) {
    var c = cookies[i];
    while (c.charAt(0) == ' ')
      c = c.substring(1, c.length);
    if (c.indexOf(searchName) == 0)
      return c.substring(searchName.length, c.length);
  }
  return null;
}

function eraseCookie(name) {
  // Erase the specified cookie
  writeCookie(name, "", -1);
}

=======================
write cookie
document.cookie = "specifiedName" + "=" + "; expires =" + new Date().toGMTString() + "; path=/";
document.cookie =
'ppkcookie1=testcookie; expires=Thu, 2 Aug 2001 20:47:11 UTC; path=/'


javascript

NaN -> not a number exception
ex. subtotal = (0 +12) * ?(uninitialized number) = NaN
isNaN(NumOfItem) -> whether the variable is a number?

subtotal.toFixed(2) -> 四捨五入至小數第二位
donutString.indexOf("dozen") != -1 -> whether "dozen" appears in the string

document.body.clientHeight/clientWidth
document.getElementById("rockImg").style.height="100px";
document.getElementById("rockImg").src= smileRock.png
圖將根據新設定的高度自行縮放,並維持比例

要補捉<span>test</span>或<div>test</div>裡的字
    alert(document.getElementById("test").firstChild.nodeValue);  ->test
    alert(document.getElementById("test").value); ->undefine (used to capture the value of text fields)
A safety way to change the text under a node
  1. remove all child nodes
  2. create a new node
  3. append the new node to its parent
ex. var node = document.getElementById("test");
     while(test.firstChild){
        node.removeChild(node.firstChild);
     }
     node.appendChild(document.createTextNode("new content"));

refresh(); ->refresh web page
setTimout ("alert('wake up');", 1000); 延遲一秒
var accept = confirm("Do you really want to delete?"); -> return true/false

if (navigator.cookieEnabled)


onblur -> The onblur event occurs when an object loses focus.  <-> onfocus
<input type="text" onblur="validateNonEmpty(this); upperCase();"></input>
function validateNonEmpty(inputField){
   if( inputField.value.length == 0)
        alert('please enter a value');
}
===using alert to inform user of validation error is a bad practice. better way===
<input type="text" onblur="validateNonEmpty(this,document.getElementById('phone_help'));"></input>
<span id="phone_help" class="help"></span>
function validateNonEmpty(inputField, helpText){
   if( inputField.value.length == 0){
        if(helpText != null){
            helpText.innerHTML = "please enter a value";
        }
        return false;
    } else{
        if(helpText != null){
            helpText.innerHTML = "";
        }
        return true;
    }
}
=============================================================

onchange -> The onchange event occurs when an object loses focus and the content of a field changes.
                  If there isn't any change, the onchange event will not be trigered.
                  Therefore, it is better to use onblur for validation.
<input type="text" onchange="calculateTotalPrice()"></input>

onresize -> The onresize event occurs when a window or frame is resized.

function myFunction(){
    var username = prompt('what is your name?');
    if(username){
        alert('hi '+username);
    }
}


css
========
visibility:hidden/visible
display:none/block

comment
// or /* /*/
id attribute -> unique within a web page
name attribute -> unique within a form
function showIt(thForm){
    alert(theForm["zipcode"].value);
}
<input   type="text"   value="第一個得到焦點"   tabIndex="1">  
<input   type="text"   value="第二個得到焦點"   tabIndex="2">  

textField.focus();
textField.select();

escape character -> \


pitfall

var num1 = document.getElementById("one").value; ->value = 3
var num2 = document.getElementById("two").value; ->value = 4
var total = num1 + num2 = ?? ->34
why? The default data type is String.
Therefore you should transform the datatype from String to Int or Float.
parseInt/parseFloat
var total = parseInt(num1)+parseInt(num2) = 7

3.12.2009

Stand-up meeting

看到一個很白爛的leader,瘋狂開會,讓我想到stand-up meeting
大致上內容如下,有機會的話,小心不要變成這個白爛的人!
開會是個溝通必要但又花時間討人厭的事情,在 Agile 方法論裡面提出了一種叫 Stand-up meeting 的每天開會方式,正如其名建議的:請站著開會,這樣才會提醒大家儘早開完。每個人至多兩分鍾報告三件事情 1.距離上次開會,我做了什麼? 2. 到下次開會,我打算做什麼 3. 我碰到的困難。如果有需要討論更細節特定的東西,請再約有關係的人在開完 stand-up 各自帶開即可。一般來說合理的 stand-up meeting 時間約10到15分鍾,至多絕不超過 30 分鍾。另外就是儘量不要等人,時間到了就開始吧。

Stand-up meeting 有幾個好處:1. 繼續專心每天的進度 2. 如果 developer 碰到任何問題,可以有機會發出聲音尋求幫助,加速事情解決 3. 幫助團隊成員瞭解其他人在做什麼事情。
」 quoted from ihower's blog

10.28.2008

oracle 11g instant client

1.下載instant client
Basic is necessary,sql plus is optional

2.配置環境變數:PATH 變數中加入 ;D:\instantclient_11_1 及增加 SQLPATH , LOCAL , TNS_ADMIN 三個使用者環境變數路徑也是對應到 D:\instantclient_11_1。接著再配置 tnsnames.ora 和 sqlnet.ora 兩個檔案Client本機應該會找不到可從安裝 Oracle Server上 Copy 過來使用,之後更改 tnsnames.ora 檔案中的 host 改成遠端 Oracle Server 的 IP 位址。

3.到hosts增加oracle server
Done!sqlplus可以連遠端資料庫了!