2011年2月17日木曜日

Android の String.indexOf(String,int) メソッドの不具合

Android の java.lang.String クラスの indexOf(String, int) メソッドには不具合がある。第一引数に空文字列を渡すと、常に 0 が返ってくる。過去のバージョンのネイティブコードで書かれた実装も、最新のバージョンの Java で書かれた実装も、同じ不具合を抱えている。

下記のケースにおいて、

  (1) "hello".indexOf("", -1)
  (2) "hello".indexOf("",  2)
  (3) "hello".indexOf("",  5)
  (4) "hello".indexOf("", 10)

(1) ~ (4) はそれぞれ、0、2、5、5 を返すべきだが、Android の String クラスの場合、全てのケースで 0 が返ってくる。


Defect of String.indexOf(String,int) method of Android

indexOf(String, int) method of java.lang.String class of Android contains a bug. If an empty string is given as the first argument, the method always returns 0. Both the native implementation in past versions and the Java implementation in the latest version contain the same defect.

Method calls of the following

  (1) "hello".indexOf("", -1)
  (2) "hello".indexOf("",  2)
  (3) "hello".indexOf("",  5)
  (4) "hello".indexOf("", 10)

should return 0, 2, 5 and 5, respectively, but the String class of Android returns 0 for all the cases.

2011年2月15日火曜日

Android で OSGi を使うべきでない理由

Android の仮想マシンである Dalvik VM がクラスのアンローディングに対応していないので、OSGi バンドルのインストールとアンインストールを繰り返すと、やがてはメモリ不足に陥り、Dalvik VM が突然終了してしまう。しかも、そのメモリ不足は、システムのRAM総量による制限ではなく、ソースコードの中で次のように固定的に定義されている定数値による制限である。

  #define DEFAULT_MAX_LENGTH  (5*1024*1024)

また、突然終了というのは、意図的に Segmentation Fault を起こすことによる Dalvik VM アボートである。

Dalvik VM のクラスローダの実装は、かなり悪い。クラスローダ毎に管理しなければならないデータを、一個のグローバル変数で管理している。ブートストラップクラスローダとユーザ定義のクラスローダの違いは無視される。たとえば、ロードされた全てのクラスは、クラスローダの区別なく、グローバル変数 gDvm.loadedClasses というハッシュテーブルに格納される。そして、このハッシュテーブルに登録されているクラスは、Mark and Sweep ガベージコレクションのマーク処理のフェーズで、そのクラスのインスタンスが残っているかどうかに関係なく、無条件にマーク対象となる。結果的に、あるユーザ定義クラスローダがロードしたクラスのインスタンス群の参照が全て無くなり、クラスおよびクラスローダへの参照が無くなったとしても、そのクラスとクラスローダはGCに回収されることはない。

原因は上記のものとは別のところにあるが、Android に付属の java.net.URLClassLoader も当然のごとく機能しない。URLClassLoader を使うと例外が発生し、そのエラーメッセージの内容は「入力データのクラスフォーマットはサポートされていない」というものであるが、これは全く見当違いのエラーメッセージであり、実際のエラーの原因は、単なる実装不備である。

Java 仮想マシンとは別の Dalvik VM 仮想マシンを作ったことは、完全に車輪の再開発であるが、再開発するならより良いものを作るべきであった。重要な部分の互換性を放棄したにもかかわらず、機能に問題がある。得たものよりも失ったもののほうが多い。

The reason that OSGi should not be used on Android

Because Dalvik VM, which is the virtual machine of Android, does not support class-unloading, if installing and uninstalling OSGi bundles are repeated, memory shortage happens in the end and Dalvik VM suddenly terminates. What is worse, the memory shortage is caused not by the limitation of the total RAM size in the system but by the constant value which is defined in source code as follows:

  #define DEFAULT_MAX_LENGTH  (5*1024*1024)

Even worse, the sudden termination is Dalvik VM Abort which intentionally causes Segmentation Fault.

The implementation of the class loader of Dalvik VM is terribly bad. Various data which should be managed per class loader are managed by one global variable. Differences between the bootstrap class loader and user-defined class loaders are ignored. For example, all loaded classes are stored into a global hash table named 'gDvm.loadedClasses' regardless of whatever class loader loaded them. And, the classes in the hash table are unconditionally marked during the mark phase of the Mark and Sweep garbage collection regardless of whether instances of the classes are still alive or not. As a result, even if no reference to instances of classes loaded by a user-defined class loader remains and if also no reference to the classes and the class loader remains, the garbage collector never collects the classes and the class loader.

The reason exists in some other places than the above, java.net.URLClassLoader which comes along with Android does not work as if it were no wonder. Using the URLClassLoader raises an exception and the error message says that the format of the input data is not supported, but the error message is utterly off the mark. The real cause is simply the flawed implementation.

It is completely reinvention of the wheel to have developed Dalvik VM which is not a Java virtual machine, but a better one should have been developed if reinvention were done. Although important compatibilities have been abandoned, problems exist in the functionaries. There is much more lost than gained.

2011年2月5日土曜日

Values returned from java.net.URL methods

URL = {Protocol}://{UserInfo}@{Host}:{Port}{Path}?{Query}#{Ref}
Authority = {UserInfo}@{Host}:{Port}
File = {Path}?{Query}

http://id:password@www.example.com:8080/profile/edit?name=Jack#123

  URL.getProtocol() = http
  URL.getUserInfo() = id:password
  URL.getHost() = www.example.com
  URL.getPort() = 8080
  URL.getPath() = /profile/edit
  URL.getQuery() = name=Jack
  URL.getRef() = 123
  URL.getAuthority() = id:password@www.example.com:8080
  URL.getFile() = /profile/edit?name=Jack

http://www.example.com

  URL.getProtocol() = http
  URL.getUserInfo() = null
  URL.getHost() = www.example.com
  URL.getPort() = -1
  URL.getPath() =
  URL.getQuery() = null
  URL.getRef() = null
  URL.getAuthority() = www.example.com
  URL.getFile() =

http://www.example.com/
 
  URL.getProtocol() = http
  URL.getUserInfo() = null
  URL.getHost() = www.example.com
  URL.getPort() = -1
  URL.getPath() = /
  URL.getQuery() = null
  URL.getRef() = null
  URL.getAuthority() = www.example.com
  URL.getFile() = /