2011年4月28日 星期四

A quick map access benchmark

I got a 10K entries big hash map and have 1k threads to traverse the map.

Single thread
Erlang ETS 3+ seconds
Java HashMap 0.18 seconds
Java ConcurrentHashMap 0.41 seconds
High-scale lib NonBlockingHashMap 0.43 seconds

1000 threads
Erlang ETS 2+ seconds
Java HashMap N/A (not thread-safe)
Java ConcurrentHashMap 0.56 seconds
High-scale lib NonBlockingHashMap 0.51 seconds

2011年4月13日 星期三

json java libraries benchmark - jsonlib, jackson, gson

Because I don't parse huge json string so I only test object binding which is much more convenient. For the same reason, I did not have jettison or gson-streaming in this test because they are much faster with the trade-off of lower level of encapsulation and OOP.

For gson and jackson, the instance of Gson and ObjectMapper can be reused. Jackson performs very good because its ObjectMapper caches class mapping meta data. Jackson will be slow if you don't reuse ObjectMapper, it's designed for reuse.

The scala built-in parser is extremely slow (100 X slower!) and lift-json is the defacto lib for scala, which is about 15% slower than jackson.




















-Bean to JSONJSON to Bean
Jsonlib 2.420583055
Gson 1.714811472
Jackson 1.7.6694667


Test env: jdk6 on Mac OS X 10.6.7
unit: millseconds

Conclusion: Jackson is the best solution for server side because of its cache. Another win of jackson is that it has zero dependency, jsonlib sucks a lot in both performance and dependency. Gson is also very fast with zero dependency, without cache, it's ad-hoc usage is the best, it's best for less heavy use of json, like android device or web start applet (170K only).

2010年5月7日 星期五

Fix eclipse corrupt index

When your eclipse index is corrupt, delete these files:

C:\pogo\p4\.metadata\.plugins\org.eclipse.jdt.core\savedIndexNames.txt
C:\pogo\p4\.metadata\.plugins\org.eclipse.jdt.core\*.index

2010年4月19日 星期一

利用Dropbox同步Squirrel SQL Client配置

安装dropbox并设置好同步, 找到这个文件
C:\Documents and Settings\<用户名>\.squirrel-sql
先备份之, 然后复制一份到你的dropbox共享目录,比如我的是"C:\Documents and Settings\danielwu\Desktop\My Dropbox\Config Files".

在Squirrel SQL client的启动脚本里最后启动java的地方加上一个参数
start "Squirrel SQL Client" ...... -Duser.home="C:\Documents and Settings\danielwu\Desktop\My Dropbox\Config Files" ......

启动Squirrel SQL看看是否正常, 如果一切正常那么,把所有需要同步的电脑都这样改脚本, 等dropbox同步好了,就可以了.

2010年4月4日 星期日

卸载微软拼音2007输入法

实在搞不懂为什么微软从Office2000开始总要搭配这个病毒一样的输入法。

1. MsiExec.exe /X{90120000-0028-0804-0000-0000000FF1CE}
2. 控制面板里删掉mspy 3
3. 注册表删掉Windows/Current_Version/IME下的东西
重启

2010年3月22日 星期一

多台电脑自动同步FoxyProxy黑白名单设置

安装dropbox并设置好同步, 找到这个文件
C:\Documents and Settings\<用户名>\Application Data\Mozilla\Firefox\Profiles\<字符若干>.default\foxyproxy.xml
先备份之.

在FoxProxy 的options --> global setting --> miscellaneous --> settings storage location

所有电脑都弄好之后, 关闭所有的firefox, 把备份的文件再复制回dropbox, 结束. 一劳永逸, 不需要导入导出了, 只要一台电脑改过你的白名单, 其他电脑自动更改.

2009年6月23日 星期二

Oracle EM Console 更改hostname后无法启动

Oracle EM Console 比较弱智,改了机器名或者IP经常就不能正常启动,用这个命令行来重新配置EM Console吧

emca -config dbcontrol db

2009年5月14日 星期四

Oracle三种常用Join

The three most commonly used joins are Indexed Nested Loops, Hash Join, and Sort-Merge Join.

Indexed Nested Loops

The Nested Loop join is an iterative join: for each row in the first (inner) row source, lookup matching rows in the second (outer) row source. If the nested lookup of the second row source performs a Unique or Range Index Scan, then we call this Indexed Nested Loops.

Indexed Nested Loops is used primarily in low volume joins; it is efficient over small volumes and versatile enough to be used in a variety of situations. Although it is fully scalable, Indexed Nested Loops is inefficient over large data volumes.

Hash Join

The hash join is used for high-volume equi-joins (joins with equals predicates). Oracle performs a single read of the smaller row source (call this T1) and builds a hash table in memory. The join key is used as the hash-key of the hash table. Then a single pass of the larger row source (call this T2) is performed, hashing the join key of each row to obtain an address in the hash table where it will find matching T1 rows.

Provided T1 remains small enough to build the hash table in memory, T2 can be scaled up to any arbitrarily large volume without affecting throughput or exceeding temp space. If T1 cannot be hashed in memory, then a portion of the hash-table spills to disk. When the hash table is probed by T2, the rows with join keys that match those parts of the in-memory hash table are joined immediately; the rest are written to TEMP and joined in a second pass. The bigger T1 is, the smaller the proportion of the hash table that can fit in memory, and the larger the proportion of T2 that must be scanned twice. This slows the Hash Join down considerably and also makes the join non-scalable.

Sort-Merge

A sort-merge join works by reading each row-source in the join separately; sorting both sets of results on the join column(s); then concurrently working through the two lists, joining the rows with matching keys. Sort-Merge is generally faster than Indexed Nested Loops but slower than Hash Join for equi-joins. It is used almost exclusively for non-equi joins (>, <, BETWEEN) and will occasionally be used when one of the row sources is pre-sorted (eg. a GROUP BY inline view)

If both row sources are small then they may both be sorted in memory, however large sorts will spill to disk making then non-scalable.

There is no way to make a Sort-Merge join scalable. The only other way to resolve a non-equijoin is to use Nested Loops, which is slower. As volumes increase, Sort-Merge will continue to out-perform Nested Loops, but will eventually run out of Temp space. The only solution is to extend TEMP, or convert the join to Nested Loops (and then wait).

2009年3月23日 星期一

Don't cache Singleton object in a serializable object

不要在可序列化对象中缓存Singleton

如果你有一个对象其中某个字段保存了一个对Singleton的引用,那么这个对象在序列化读取后会导致在同一个虚拟机里Singleton对象有两个。比如下面的例子

public class Test3 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        System.out.println(ABC.getInstance());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        new ObjectOutputStream(out).writeObject(ABC.getInstance());
        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
        ABC abc = (ABC) in.readObject();
        System.out.println(abc);
        
    }
}

class ABC implements Serializable {
    private ABC() {}
    private static ABC instance = new ABC();
    public static ABC getInstance() {
        return instance;
    }
}

你会发现,打印出的两个ABC的instance是不一样的。解决方法很简单,在ABC加入这个方法
    protected Object readResolve() {
        return instance;
    }

这样可以重置instance到这个虚拟机中的Singleton实例,还可以把引用instance的字段作为transient字段节省IO时间。
但是这不是最好的办法,最好就是,如果你知道ABC是Singleton,那么就永远不要把ABC赋值到你的对象成员变量里,getInstance()因为是static方法,编译器通常会做inline的,频繁调用不会导致频繁方法栈操作,所以缓存它意义不大。

2009年3月22日 星期日

Literal Pitfall

我们过去经常使用这样的方式定义常量, 比如我最不喜欢的java.util.Calendar类里面定义月份有
public static final int     APRIL  =   3
public static final int     MAY    =   4
public static final int     JUNE   =   5
...

我相信很多人也是这样定义常量或者枚举型。其实这样会有一个很严重的问题,和编译器的行为有关系。VM Spec 2.17.4中描述类初始化的发生条件时提到ClassA的某个常量字段比如ClassA.MAX被访问的时候不会导致ClassA类被初始化。

2.17.4 Initialization
........
A class or interface type T will be initialized immediately before one of the following occurs:

    * T is a class and an instance of T is created.

    * T is a class and a static method of T is invoked.

    * A nonconstant static field of T is used or assigned. A constant field is one that is (explicitly or implicitly) both final and static, and that is initialized with the value of a compile-time constant expression. A reference to such a field must be resolved at compile time to a copy of the compile-time constant value, so uses of such a field never cause initialization.

原因是如果ClassB引用ClassA.MAX,编译器会把ClassA.MAX的常量值复制到ClassB的常量池中。这样显然效率更高。

我们做一个实验,有两个类
public class ConstClass {
    public static final int TEST = 5;
}

public class RefClass {
    public static void main(String[] args) {
        System.out.println(ConstClass.TEST);
    }
}
编译后,执行RefClass明显应该打印5,我们用javap看一下pesudo code:
public class RefClass extends java.lang.Object{
public RefClass();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   iconst_5
   4:   invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   7:   return

}

这里你会看到iconst_5,RefClass并没有让VM加载ConstClass,事实上,你删除ConstClass.class也没有关系。
问题来了,如果你这样定义常量或者枚举值,将来如果ClassA.MAX的值你需要更改,那么你必须重新编译所有引用过这个值的类!那些类需要重新编译,这是非常难预测的,尤其是被频繁使用的API.

如何克服呢?有两种方式,一种是提供一个getTEST()来返回常量值,比如把刚才的类改成
public class ConstClass {
    public static final int TEST = 5;
    public static int getTEST() {
        return TEST;
    }
}
public class RefClass {
    public static void main(String[] args) {
        System.out.println(ConstClass.getTEST());
    }
}
由于getTEST()是static,编译器可能会inline他,因此效率不会太低

还有一种方式,是jdk1.5之后提供的enum
让我们重新写这两个类
public enum ConstClass2 {
    TEST(5);    
    private int value;
    ConstClass2(int v) {
        this.value = v;
    }
    public int getValue() {
        return this.value;
    }
}

public class RefClass2 {
    public static void main(String[] args) {
        System.out.println(ConstClass2.TEST.getValue());
    }
}
在用javap看看引用类
public class RefClass2 extends java.lang.Object{
public RefClass2();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   getstatic       #3; //Field ConstClass2.TEST:LConstClass2;
   6:   invokevirtual   #4; //Method ConstClass2.getValue:()I
   9:   invokevirtual   #5; //Method java/io/PrintStream.println:(I)V
   12:  return

}
你会发现这次5没有被复制到引用类的常量池,相反getstatic代替了iconst_,相当于ClassA.MAX会被解释成ClassA.getMAX(),这样效果其实和上面说的另外一种方法getTEST()是类似的。

其实enum还有其他的好处,JDK guide中说 (http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html)

  • Not typesafe - Since a season is just an int you can pass in any other int value where a season is required, or add two seasons together (which makes no sense).
  • No namespace - You must prefix constants of an int enum with a string (in this case SEASON_) to avoid collisions with other int enum types.
  • Brittleness - Because int enums are compile-time
    constants, they are compiled into clients that use them. If a new
    constant is added between two existing constants or the order is
    changed, clients must be recompiled. If they are not, they will still
    run, but their behavior will be undefined.
  • Printed values are uninformative - Because they are
    just ints, if you print one out all you get is a number, which tells
    you nothing about what it represents, or even what type it is.

其中第三点就是本文描述的问题,另外typesafe也是个问题,比如你完全可以把Integer.MAX_VALUE 传给 Calendar.set(Integer.MAX_VALUE, somevalue),另外没有命名空间而且打印出来也很不友好。

所以,总之,还是enum吧?

2009年2月16日 星期一

SetPoint Ctrl/Shift 粘滞的解决

我把罗技MX Revolution鼠标的查找键设置成为了快速关闭,但是后来发现关闭会发送Alt+F4到应用程序,对于多tab的程序,比如firefox, notepad++会关闭掉整个程序,而不是一个tab,所以我改成了Ctrl+F4,然后问题就来了,有时候Ctrl+F4关闭窗口的时候,Ctrl会一直被按下去,除非你再按两下Ctrl.

解决方法一共三种,来源于http://www.mstar.net/users/rlowens/?n=SetPoint.StuckModifiers,最后一种最好用,如下
1. Set a button to Keys:Close (Alt+F4) in SetPoint
2. Close SetPoint from the System Tray
3. Edit your setting file (in notepad.exe or the like). Start->Run->\Application Data\Logitech\SetPoint and then edit the user.xml file in that folder (see below).
4. Restart c:\Program Files\Logitech\SetPoint\SetPoint.exe or reboot

For Ctrl+W, change:

< Handler Class="KeyStroke" >
< Param KeyName="%{F4}"/ >
< /Handler >

to:

< Handler Class="KeyStroke" >
< Param KeyName="^w"/ >
< /Handler >


The modifiers are:

^ ctrl
+ shift
* win
% alt

Some extended keys are:

* {PGDN}
* {PGUP}
* {TAB}
* {ESC}
* ~ (Enter)
* {BKSP}
* {RIGHT}
* {LEFT}
* {F1-F16} works (F17-F24 don't work, have to use the KeystrokeAssignment handler instead)

2009年2月10日 星期二

使用Oracle开发国际化的应用,字符长度问题

最早是在和IBM的一个项目做接口的时候,发现了一些在开发国际化系统时忽略的一些Oracle的问题。

首先说一下字符长度的定义

1. 对于ISO8859 和 ANSI/ASCII: 都是1个字符就是1 byte 但是 ANSI/ASCII是7 bits,所以有128个基本字符,而ISO8859是8 bits,而且有多个集合,因此ISO8859包含ANSI/ASCII,然后还提供了128个额外特殊字符,比如英镑符号
2. 对于GBK/GB2312: 1个字符是2 bytes
3. 对于UTF-8: 1个字符是3 bytes

有了这个知识,你就明白了,其实在国际化的应用中定义字符的长度不是随意的。如果你随便定义了一个列是varchar2(30),这个30是什么意思呢?你可能认为是30个字符,其实不是,是30 bytes!

这样的话,如果Oracle默认编码是UTF8,如果你要对这个列插入一个字符串包含8个中文和6个英文,那么这个字符串是8*3+6=30个字节,你可以插入,但是如果这个字符串包含8个中文和7个英文,那么这个字符串是8*3+7=31个字节,你就无法插入了。这样的话,你在界面里很难控制字符串长度的校验。

怎么解决呢?有三种办法

1. 最好的办法就是用nvarchar2(30),这个定义表示30个字符,编码方式只可以选utf8或者utf16
2. 使用UTF8作为默认编码,创建表的时候指定varchar2(30 char)显式表示这是30个字符,不是30个byte
3. 修改Oracle参数nls_length_semantics=CHAR,然后再创建表结构,这样的话varchar2(30)表示varcahr2(30 char).但是这个参数不影响已经创建的表,只能用于新建的表。

2009年2月9日 星期一

2009年1月18日 星期日

用 PhantomReference 避免OutOfMemory

PhantomReference比Weak/Soft的引用强度都要低,PhantomReference.get()总是返回null,为什么呢?

其实PhantomReference而要是不可缺少的重要引用类型, 我们知道WeakReference在finalize方法调用或被gc清理之前会进入ReferenceQueue, 这时候没有任何strong reference引用对象,然后可以通过ReferenceQueue去做收尾工作。但是在finalize()方法,或者重新给这个对象一个引用使它reachable(从某个活动线程调用栈,或者静态变量可达), 使这个对象延长生命周期暂时不会被gc清理。而PhantomReference只会在对象被从内存中清除后才会进入队列,get()总是返回null (ReferenceQueue通知一个PhantomReference的时候,既然内存都已经物理释放了,当然也无法给你一个对象,所以get总是返回null也有这个原因), 所以你没有办法重新使这个对象再次reachable。PhantomReference只是提供了一种方式让你跟踪一个曾经产生过的对象,由此让你知道这个对象到底有没有被物理的清除。

比如当你的applet程序需要处理一个非常大的图片的时候,你可能希望图片处理结束并且内存被释放之后再处理下一个图片。如果用PhantomReference来引用上一个图片对象,当ReferenceQueue通知你的时候,你就可以知道上一个内存对象已经被物理清除,你可以继续下一个大内存对象的处理了。这样就可以避免由于GC线程优先级低导致上一个大内存对象还没有释放下一个大内存对象又被创建,让OutOfMemoryError出现的概率低一些。

还有一个好处,就是PhantomReference比用finalize方法好的多,因为VM对finalize的处理不如PhantomReference简单可靠,只不过是你要写的代码稍微多一点点而已。


Ethan有一个更完整的对四种reference的解释 http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html