Saturday, May 07, 2005

NerdTests

เดี๋ยวคุณ bact กับ คุณ Keng จะไม่เชื่อ
เอารูปมาให้ดูเลย
จะได้ไม่โดนข้อหาแต่ง javascript :)

mynerd

ภรรยาผมเดินผ่านมา เลยบังคับให้ทำด้วย
ผลลัพท์คุณเธอได้ 12 แต้ม

Related link from Roti

Thursday, May 05, 2005

swt + opengl plugin

OpenGL Plug-in

Article ของ Eclipse OpenGl Plugin
Using OpenGL with SWT

สำหรับผู้ที่ใช้ swing ก็มีตัวที่คล้ายๆกันคือ Jogl

สุดท้ายก็ ariticle ที่พูดถึง java3D กับ java-opengl

Related link from Roti

Star Wars Asciimation

http://www.asciimation.co.nz/

Related link from Roti

j4K

J4KJava Game Contest
game must be 4096 bytes or under.

ตัวอย่างเกมส์
http://www.cokeandcode.com/4k/tilt

Related link from Roti

MIR

Music Information Retrieval
M2K (java opensource)
Tools

เห็นแล้วน่าสนใจดี (แต่ชาตินี้คงไม่ได้ใช้)
คุณ bact น่าจะชอบ

Related link from Roti

Swing Pointers

Link

Related link from Roti

Memory Leak TestCase

ตัวเนื้อหาได้มาจาก blog ของ Tim Boudreau

ปัญหาที่พบบ่อยในการเขียน Swing ก็คือ Memory leak
ซึ่งเกิดการการ add listener ระหว่าง component ต่างๆ
แล้วไม่มีการ remove ออก

ใน project netbean ซึ่งเป็น swing ขนาดใหญ่
ก็มีการจัดการกับปัญหานี้ผ่านทาง testcase + INSANE
โดย testcase ของเขามี pattern ดังนี้

SomeObject o = new SomeObject();
something.doSomethingWith (o);
WeakReference ref = new WeakReference (o);
o = null;
assertGC ("Object still referenced", ref);

object o เป็น object ที่เราสนใจว่ามีการ leak
เกิดขึ้นหรือไม่ โดยสังเกตุว่ามีการสั่ง o = null ซึ่ง
เป็นการ clear referenece ที่อ้างถึงภายใน method นี้แล้ว
ดังนั้นถ้ามี gc เกิดขึ้นเมื่อไร object o ควรจะหายไปจาก Heap
ซึ่งวิธีการตรวจสอบก็คือ wrap object o ด้วย
WeakReference ก่อนแล้วค่อยส่งไปตรวจด้วย method assertGC

ใน method assertGC มี code เขียนไว้ดังนี้
    public static void assertGC(String text, java.lang.ref.Reference ref) {
ArrayList alloc = new ArrayList ();
int size = 100000;
for (int i = 0; i < 50; i++) {
if (ref.get() == null) {
return;
}
System.gc();
System.runFinalization();
try {
alloc.add (new byte[size]);
size = (int)(((double)size) * 1.3);
} catch (OutOfMemoryError error) {
size = size / 2;
}
try {
if (i % 3 == 0) Thread.sleep(321);
} catch (InterruptedException t) {
// ignore
}
}
alloc = null;
fail(text + ":\n" + findRefsFromRoot(ref.get()));
}

หลักๆ ก็คือวน loop 50 ครั้งเพื่อ check ว่า
เจ้า object ที่อยู่ใน weak reference หายไปหรือยัง
โดยแต่ละครั้งก็พยายาม force gc ให้เกิด
โดยมีการ simulate การใช้ heap ผ่านทาง
ArrayList alloc

กรณีที่ loop ครบ 50 ครั้งแล้ว
ref.get() != null
ก็แสดงว่า memory leak แล้ว
ก็ call findRefsFromRoot ซึ่งมีการทำงานดังนี้
    private static String findRefsFromRoot(final Object target) {
final Map objects = new IdentityHashMap();
boolean found = false;

Visitor vis = new Visitor() {
public void visitClass(Class cls) {}

public void visitObject(ObjectMap map, Object object) {
objects.put(object, new Entry(object));
}

public void visitArrayReference(ObjectMap map, Object from, Object to, int index) {
visitRef(from, to);
}

public void visitObjectReference(ObjectMap map, Object from, Object to, java.lang.reflect.Field ref) {
visitRef(from, to);
}

private void visitRef(Object from, Object to) {
((Entry)objects.get(from)).addOut(to);
((Entry)objects.get(to)).addIn(from);
if (to == target) throw new RuntimeException("Done");
}


public void visitStaticReference(ObjectMap map, Object to, java.lang.reflect.Field ref) {
((Entry)objects.get(to)).addStatic(ref);
if (to == target) throw new RuntimeException("Done");
}
};

try {
ScannerUtils.scanExclusivelyInAWT(ScannerUtils.skipNonStrongReferencesFilter(), vis, ScannerUtils.interestingRoots());
} catch (Exception ex) {
// found object
found = true;
}

if (found) {
return findRoots(objects, target);
} else {
return "Not found!!!";
}
}

ซึ่งเป็นการ dump เอา Reference ทั้งหมดออกมา
ผมยังไม่ได้หา javadoc ของ ScannerUtils ดู
แต่เดาว่า skipNonStrongReferencesFilter คงจะ
เป็นการ scan หาเฉพาะพวกที่อยู่ใน WeakReference เท่านั้น

ที่นี้ลองดูตัวอย่างที่ Tim เขาเขียนไว้
public class MyFrame extends javax.swing.JFrame {
MenuAction menuAction = new MenuAction();
public MyFrame() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setBounds (20, 20, 300, 300);
getContentPane().addMouseListener (new MouseAdapter() {
public void mouseReleased (MouseEvent me) {
getPopupMenu().show((Component) me.getSource(), me.getX(), me.getY());
}
});
}

JPopupMenu getPopupMenu() {
JPopupMenu menu = new JPopupMenu();
menu.add (new JMenuItem (menuAction));
return menu;
}

static final class MenuAction extends AbstractAction {
public MenuAction() {
putValue (Action.NAME, "Do Something");
}
public void actionPerformed (ActionEvent ae) {
System.out.println("Action performed");
}
}
}

ผลลัพท์ที่ได้จากการ Test ออกมาหน้าตาประมาณนี้
Testcase: testLeak(javaapplication7.MyFrameTest):       FAILED
Popup menu should have been collected:
private static java.awt.Component java.awt.KeyboardFocusManager.focusOwner->
javaapplication7.MyFrame@d1e89e->
javaapplication7.MyFrame$MenuAction@f17a73->
javax.swing.event.SwingPropertyChangeSupport@3526b0->
javax.swing.event.EventListenerList@3ddcf1->
[Ljava.lang.Object;@105c1->
javax.swing.JMenuItem$1@f74864->
javax.swing.JMenuItem@110003->
javax.swing.JPopupMenu@627086

จะเห็นว่า leak ครั้งนี้ขึ้นใน JMenuItem
ของ JMenuItem ยัง hold reference ถึง MyFrame$MenuAction อยู่
โดยตัวที่ทำให้ leak ก็คือ menu.add(new JMenuItem(menuAction))
ซึ่งกรณีที่เรา new JMenuItem จะเกิดการ add listener
ระหว่าง MenuAction กับ MenuItem
ผลก็คือ PopupMenu ยังมี link จาก menuItem วิ่งไป menuAction

สรุป

โดยปกติเวลาเราสงสัยว่า memory leak หรือไม่ก็ต้องใช้
พวก Profiler ทำการใล่ trace ดู
แต่ที่ Tim เขาแสดงให้ดูก็คือ เราสามารถเขียน Testcase
เพื่อดักจับไว้ได้ก่อนเลย ทำให้เมื่อ project evolve
ไป ถ้าเกิดมีใครมาแก้อะไรแล้วทำให้ memory leak
ตัว testcase ก็จะฟ้องออกมาทันที

Related link from Roti

qemu

วันนี้อ่านเจอ Qemu
ซึ่งเป็น opensource emulator
ก็เลยทดลอง run linux บนเครื่อง mac

โดยจะทดลอง run knoppix ดู
1. เริ่มแรกก็สร้าง knoppix cdrom image file
สั่ง umount cdrom ก่อน
sudo umount /Volumns/KNOPPIX

ใช้ dd สร้าง image file
sudo dd if=/dev/disk1s0 of=/tmp/testimage.img bs=2048


2. ใช้ qemu run จาก image file
qemu -cdrom /tmp/testimage.img -pci -m 512 -k en-us  -boot d 


knoppixonmac
ความเร็วที่ได้นั้นไม่เป็นที่น่าประทับใจแต่อย่างไร

Related link from Roti

Tuesday, May 03, 2005

Linux บน Mainframe

วันนี้ได้มีโอกาสสัมผัส linux บน ​Mainframe
โดยมีหน้าที่ standby เพื่อเกิดปัญหาในการ
ย้าย Web Application จาก AIX Unix Server
ไปยัง Mainframe

ตัว Architecture มีลักษณะดังนี้
ที่ชั้นแรกสุดก็คือชั้นของ LPAR (Logical Partition)
ซึ่งก็คือการแบ่ง computer resources (processors, memory, storage)
ออกเป็นส่วนๆ โดยแต่ละส่วนก็จะมี Operating System
ของตัวเอง
ชั้นที่ 2 ก็คือ VM (virtual machine) ไม่ใช่ Java Virtual Machine นะ
แต่เป็น Virtual machine ที่เปิดโอกาศให้เรา
ติดตั้ง Virtual Server หลายๆตัวใน VM ได้
(แตละตัวก็จะมี virtual cpu, virtual memory, I/O, network
ของตัวเอง)
ขั้นที่ 3 ถึงจะเป็น Linux
โดยที่นี่ใช้ SUSE Linux Enterprise Server 8

หลังจาก Telnet ได้ เริ่มแรกสุดก็ทดสอบ Java Benchmark ก่อนเลย
โดยใช้ Scimark2
ได้คะแนน 66 คะแนน
ยิ้มเลย notebook เรายังได้แต้มตั้ง 200 นิดๆเลย
ด้วยความไม่แน่ใจก็เลยลองไป run ทดสอบ
บน Aix Unix Server ด้วย ปรากฎว่าเจ้า Power PC
ทำคะแนนได้ 266 คะแนน
หลังจากสับสนงงงวยกับคะแนนที่ได้ ก็เลย
ไปหา Red Book ของ IBM มานั่นอ่านดู
ก็พบว่า ตัว linux บน Z os นั้นเหมาะกับงานที่มีลักษณะ
I/O bound สลับกับ CPU bound แต่ไม่เหมาะอย่างยิ่ง
กับงานประเภท Deep Computing หรือ cpu intensive

เริ่มแรกสุดก็เจอปัญหาไม่สามารถต่อ jdbc ไป db2 ได้
มีข้อความว่า unsatisfied linkage error ใล่หา
สักพักจึงเจอว่า
ตัว SUSE เป็น 64 bit
ตัว WebSphere เป็น 31 bit (ไม่ได้พิมพ์ผิดนะ 31 bit)
ส่วน db2 client เป็น 64 bit
ปัญหาเกิดเมื่อ java ที่มากับ Websphere พยายาม
ที่จะต่อ jdbc ซึ่งจะต้องมีการเรียก native code
ของ db2 client ซึ่งเป็น 64 bit
ก็เลยขอให้คนของ ibm ลง db2client
version 31 bit ให้แทน

ปัญหาต่อไปก็คือเวลาที่ใช้ในการทำ I/O
เริ่มตั้งแต่การ FTP ตัวฐานข้อมูล ซึ่งช้ามากๆ
จากนั้นก็เป็นการ create table space
ของ db2 (เนื้อที่ประมาณ 30 GB)
ซึ่งก็ใช้เวลาไปอีก 1 ชั่วโมงครึ่ง
น้องชิ้มแจ้งว่า Data ชุดเดียวกันนี้
เคยเอาไปลงที่เครื่อง PC เวลาที่ใช้ในการ
create table space กับ copy data
ใช้เวลารวมกันแค่ 22 นาที
ด้วยความสงสัยก็เลยเอา Bonnie
มาทดสอบเปรียบเทียบกับ unix server
ปรากฎว่า write I/O ตัว unix กินขาด
(เผอิญว่าลืมเอา thumbdrive ที่บันทึกผลลัพท์กลับมา
ไว้ได้กลับมาเมื่อไรจะ post ให้ดูอีกที)
ก็เลยแจ้งคนของ IBM ว่าช่วยดูให้ด้วย
config อะไรผิดหรือเปล่า

ปัญหาถัดไปก็เป็นเรื่องของ web server
ซึ่งเราจำเป็นต้องใช้ mod_gzip ด้วย
ตัว IBM http Server ที่ bundle มากับ Websphere นั้นไม่
สามารถใช้ mod_gzip ได้
ก็เลยต้องใช้ Apache แทน
แต่ปรากฎว่าเกิดปัญหา conflict ของ 64 bit กับ 31 bit
อีกแล้ว
เนื่องจาก so ของ websphere เป็นแบบ 31 bit
ส่วน apache ที่มากับ SUSE เป็น 64 bit
ก็เลยลอง load source code ของ apache
มา build เองโดยกำหนด CFLAGS=-m31 เพื่อบังคับ
ให้ gcc compile ออกมาเป็น 31 bit
แต่ก็ไปตายตอน ld อยู่ดี
ในส่วนนี้ก็เลยให้คนของ ibm (อีกแล้ว)
ไปจัดหา Ibm HttpServer version
ที่ใหม่กว่า 1.3.31 และเป็น 31 bit มาให้

นอกจากนี้ยังพบปัญหาเกี่ยวกับการ FTP
ที่ส่ง file ได้สำเร็จแต่เกิดมีเนื้อหาบางส่วน
ผิดเพี้ยนไป
แต่ปัญหาในส่วนนี้คาดว่าไม่เกี่ยวข้องกับ linux บน Mainframe

ส่วนปัญหาหนักๆที่ยังไม่ทดสอบ
ก็จะเป็นเรื่อง Jasper Report ที่น่าจะมีปัญหาในส่วน
AWT Thread กับ X Window

สรุปเบื้องต้น
ก็คงต้องอวยพรให้บริษัทที่ปรึกษา ผู้ซึ่งแนะนำให้
move infra structure ขึ้นไปอยู่บน Mainframe
จงโชคดีในการย้ายด้วยเทอญ
ส่วนตัวผมนั้นรู้สึกหวั่นๆอยุ่บ้าง เพราะดูท่าแล้ว
resources ที่มีให้มัน fit เปรี๊ยะเสียเหลือเกิน
(แถมยังแพงด้วย มี paper อยู่อันบอกไว้ว่า
ด้วยราคาของ mainframe แล้ว เราต้อง
cosolidate linux อย่างน้อย 32 server
จึงจะเริ่มมี cost ที่สูสีกัน)

ปล. ขอนินทาที่ปรึกษา (ไม่บอกนะว่าอาจารย์
มหาลัยไหน) และคนของ ibm
ด้วยเถอะ สมัยที่เริ่มต้นโครงการใหม่ๆ (เมื่อต้นปีก่อน)
ทั้งที่ปรึกษาและคนของ ibm บอกว่าเครื่อง
database server ควรใช้ 4 cpu
ตัว application server ควรใช้ 2 cpu
ส่วนผมยืนยันและนั่งยันว่าผิด มันต้องกลับกัน
ผลลัพท์ก็ prove ไปแล้ว
ปรากฎว่า load ปัจจุบัน ถ้าใช้คำสั่ง sar ดู
เครื่อง App Server มี cpu load อยู่ที่ 40-60 %
ส่วน DB Server มี cpu load -> 5 % (tune ไว้ดี)

Related link from Roti

Monday, May 02, 2005

Absenter

http://www.absenter.org/
เกี่ยวกับ Photographs อีกแล้ว

ลองดู Design เขาสิครับ
ใช้รูปเป็นหลัก
ที่เหลือ (link, banner, note) เปลี่ยนสีไปตามรูป
ชอบตรงนกที่เกาะเหนือตัว R

Related link from Roti