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.