続:ローカル変数へのnull代入
FindBugsのmlで質問してみたら、Sunのエンジニアから回答があって「Hot soptと認識されないと効かないよ」とのことだったので、手元のテストコードを書き換えて検証してみた。
public class Test {
int[] data = new int[1000000];
public static void main(String[] args) throws Exception {
Runnable r = new Runnable() {
public void run() {
for (int i = 0; i < 200; i++) {
foo(0);
if ((i % 10) == 0) System.out.println("...(" + i + ")");
}
System.err.println("***");
while (true) {
foo(500);
}
}
void foo(int i) {
Test test = new Test();
if (i > 0) {
try {Thread.sleep(i);} catch (InterruptedException ex) {}
}
}
};
for (int i = 0; i < 10; i++) {
new Thread(r).start();
}
Runtime runtime = Runtime.getRuntime();
while (true) {
System.gc();
System.out.println("Using memory:" + (runtime.totalMemory() - runtime.freeMemory()));
Thread.sleep(1000);
}
}
}
200回ほど、がむしゃらに回ってVMにHot spotと認識させておいてから、あとはゆったりと回って、使用メモリ量を確認。
JDK1.5.0だと、コンスタントに40MBくらい使い続けるけど、 Using memory:40230672 Using memory:40189568 Using memory:44249648 ...
JDK1.6.0-b2だと、なんと160KBくらいに減った。 Using memory:155096 Using memory:155096 Using memory:155096 ...
ちなみに、最初の繰り返し回数を100にすると、効果がなかったので、Hot spotと認識される回数は100と200の間にあるようだ。
@DepreactedのTARGET
@DeprecatedのTARGETが無指定ってことは、何にでも付けられるわけで。ローカル変数に付けたら、どうなるんだろ。
public class Test {
public static void main(String[] args) {
@Deprecated final int i = 100;
class Foo {
void foo() {
System.out.println(i);
}
}
}
}
ん〜、なにも起きない。^^; なんでローカル変数を除外しなかったんだろうか。何か利用シーンがあるのかな。
DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION
「デフォルトのretentionレベルのアノテーションの有無をリフレクションで確認することはできません。」
Method m = String.class.getMethod("getBytes", new Class[] {int.class, int.class, byte[].class, int.class});
if (m.isAnnotationPresent(Deprecated.class)) {
System.err.println("String.getBytes(int, int, byte[], int) is deprecated.");
}
みたいなコードで確認すると、思いっきり警告される。DeprecatedのリテンションはRUNTIMEなんですけど…。
アノテーションクラスのデータベースをFindBugsの中で持っていて、ここへの登録はFindBugsがvisitしたアノテーションのみが行われるようだ。なので自作アノテーションで、それがFindBugsの検査対象に入っていれば、正しく処理されるけど、そうでないアノテーションは、みんな警告されてしまう。最新のソースを確認しようと思ったら、SourceForge落ちてるし。明日見てみよう。
京浜東北線
夕方、会議で川崎から東京に移動。川崎駅に行ったら東海道線は15分待ち。ここは本当に東京ですか(<違うよ)。これなら京浜東北線の方が快速だし、速いかも。と思ったら、各駅停車するんですけど。京浜東北線って快速運転やめちゃったの?
東京駅からの錦糸町行きバスも渋滞のせいか全然来ないので、猛烈に暑い中、三越前まで歩き。結局箱崎まで1時間以上かかったよ。なんか大量に時間を無駄にした気分。やっぱりわざわざ移動するなんて、ばかばかしい。今度からは電話会議にしよ。
ローカル変数へのnull代入
FindBugsの新しく追加されたメッセージに、こんなのがあった。
The code stores null into a local variable, and stored value is not read. This store may have been introduced in assist the garbage collector, but as of Java SE 6.0, this is no longer needed or useful.
このコードはローカル変数にnullを代入していますが、この変数は、その後読み出されていません。恐らくnullを代入することで、ガーベージコレクタにヒントを与えることを意図したと思われますが、Java SE 6.0からは、このような記述は不要です。
確かに原理的には、可能な気がするんだけど、手元で色々やってみても、それらしい動きは確認できなかった。まだβ2だからかなぁ。
同じcase
ふたつのcaseラベルに同じ処理が書かれている場合に警告してくれるディテクタがFindBugsに追加されたのだが、どうも正しく動かないのでパッチを送ってみた。
switch-caseはバイトコード上は、こんな感じのテーブルが生成される。
1: lookupswitch{ //2
0: 28;
1: 37;
default: 46 }
書かれているのはバイトコード上のオフセット。で、この範囲にあるコードの比較をしているようだ。この時、このオフセットを格納する配列サイズが、なぜか1大きく、誤動作していた。で、この方法だと最後のcase(この場合はdefault)の終りが分からないので、最後のcaseだけ対象外になってしまう。これは解決方法が分からなかった。
ちょっと面白かったのはバイトコードの比較。
byte[] clause = getCodeBytes(method, switchPos[i], switchPos[i+1]); BigInteger clauseAsInt = new BigInteger(clause); Collectionvalues = map.get(clauseAsInt); if (values == null) { values = new LinkedList (); map.put(clauseAsInt,values); }
バイト配列をBigIntegerに放り込んでMapでチェック。これは思いつかないな。
Ebook Online Store
なんかPDF版の本が、$10とかでごろごろしているんですけど…
とりあえずWebWork In Actionを購入。う〜むSafari Bookshelf解約しようか。最近、ちっとも新しい本が入らないし。
Java Concurrency in Practice
紙ものは結構高いし、かさばるし検索が不便だし、pdf版が出ないか探していたわけだが、なんだ。USのAmazonにpdf版があるじゃん。灯台もと暗しってやつだ。
DRMってのが、ちょっとひっかかったけど思い切って注文。クレジットカードで日本からでも、あっさり購入成功。閲覧は、.net passport(無料)が必要。あと、Adobe Readerのアクティベーションが必要(無料。ファイル->電子書籍->デバイスのアクティベート)。
とりあえず、2台のPCでも普通に閲覧できているし、オフラインでも読めているので、安心。このくらいのゆるいDRMなら全然okかな。あ、でもLinuxとかだと読めないんだろうか。あとはどうやって読む時間を捻出するか。
この動作を保証するわけではないので、購入されるかたは、ご自身のリスクでどうぞ。
セッタインジェクション嫌い
セッタインジェクションは不変式が守れなくなる(守りにくくなる)から嫌い。それならコンストラクタインジェクションはというと、これならまぁいいんだけど、でも、あまり引数が増えてくるとねぇ。どれがどれだか良くわかんなくなし。インジェクション使わない場合には、コンストラクタ呼び出し前に、引数を全部揃えておかないといけないから、やたらとローカル変数が増えるし。
引数転送オブジェクト(?)ってのはどうだろうか。
public class Test {
public static class Arg {
private boolean freezed;
private String arg1;
private int arg2;
// ...
public Arg setArg1(String arg1) {
if (freezed) throw new IllegalStateException("This argument is already frozen.");
this.arg1 = arg1;
return this;
}
public Arg setArg2(int arg2) {
if (freezed) throw new IllegalStateException("This argument is already frozen.");
this.arg2 = arg2;
return this;
}
// ...
private String getArg1() {
freezed = true;
return arg1;
}
private int getArg2() {
freezed = true;
return arg2;
}
}
private String arg1;
private int arg2;
public Test(Arg arg) {
arg1 = arg.getArg1();
arg2 = arg.getArg2();
}
public static void main(String[] args) {
Test.Arg arg = new Test.Arg();
arg.setArg1("Hello")
.setArg2(100);
new Test(arg);
arg.setArg1("World"); // IllegalStateException
}
}
インジェクションしたい時は、Argに対してセッタインジェクションすれば良い。こいつは振る舞いを持たないから、別に不変式は不要。もう読んじゃった後に設定される混乱を避けるため、読み出されたら状態をfreezeしてしまって、それ以降の設定はエラーにする。もちろん継承も可能。
public class SubTest extends Test {
public static class SubTestArg extends Arg {
private short arg3;
//...
}
public SubTest(SubTestArg arg) {
super (arg);
// ...
}
}
privateも結構いいかも。
単体テストの利便性を考えてパッケージプライベートを多用していたけど、別にprivateだってリフレクション使えばアクセスできるんだから、テスト用のヘルパメソッドを用意すれば済む話なわけで。やっぱり「テストが簡単になるから」なんて理由でパッケージプライベートにするのは、間違っているような気がしてきた。特に人のコードをいじる段になると、privateメンバなら、影響範囲が、そのクラスの中だけに限定されるので、格段に楽になる。あれほど苦々しく感じていたメンバprivate強制コーディングルールが救世主にさえ見えてくる。とはいえprivate強制もどうかとは思うけど。まぁいずれにせよ、ちょっとテストが楽になるなんて理由でパッケージプライベートにするなんていうのは、やっぱり間違いかもしれない。
jigブラウザ解約
最近RSSリーダのDELCOがまともに見えなくなってしまった。他のRSSリーダも色々試してみたけど、まともに見られるものがなかったので、jigブラウザを解約した。
昔ちょっと使っていた、ぐるっぽをもう一度使ってみたら結構良くなっていた。昔は、いちいちログインが必要だったのだけど、今はmixiのモバイル版のようにボタン1つでログインができる。ユーザ/パスワードを入れる必要がないのだ。DELCOもぐるっぽ経由で閲覧できるのだけど、DELCOへのログインが必要なのが難点。ぐるっぽはサーバ側でクッキ管理するのだけど、セッション管理なため、ブラウザを終了するとサーバ側のクッキも消えてしまうようだ。
しかしDELCOのログインページはリファラとかチェックしていないようで、自分のところにDELCOへの自動ログインページを置いてみたら、あっさりログインできた。しばらくこれで行ってみよう。これなら月々jigに600円払わなくていいし、音楽聴きながらでもブラウザ使えるし。
@edu.umd.cs.findbugs.annotations.SuppressWarnings
こんなアノテーション出来ていたんだ。
public class Test {
int i;
public int foo() {
return i;
}
}
L C UwF: 書かれないフィールド Test.i があります。 In Test.java
アノテーションを追加すると警告が出なくなる。
public class Test {
@edu.umd.cs.findbugs.annotations.SuppressWarnings
int i;
public int foo() {
return i;
}
}
ターゲットは
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,
ElementType.PARAMETER, ElementType.CONSTRUCTOR,
ElementType.LOCAL_VARIABLE, ElementType.PACKAGE})
今ごろ気付いたんだけど、アノテーションってパッケージにも付けられるのか。って、なんか良く分からないな。別にパッケージファイルみたいなものがあるわけでもないし、こんな情報、どこにリテンションされるんだろうか。調べてみると言語仕様3.0の7.4.1.1に書いてあった。仕様上は、パッケージアノテーションは、パッケージ内の1クラスファイルにのみ指定可能のようだ。ってことは実行時にパッケージアノテーション情報を見ようとすると、パッケージ内の全クラスファイルを順番に調べて(ロード済みであればクラス情報を見るだけ)初めて見つかったものを返すような感じなんだろうか。
混乱を避けるためにpackage-info.javaというファイルにしておくのがrecommendされている。
Lawsonの店員ってすごい。
大崎のゲートシティの中にLawsonがあるんだけど、レジ打ちの店員が、ずっと宣伝のために大きな声で、おすすめ商品とか紹介しているんだよね。すげ〜。あんなこと自分がやったら、絶対釣り銭、間違えるな。
Cygwin bashからエクスプローラ呼び出し
explorer `cygpath -d $*`
'x'とか短いファイル名のシェルスクリプトにしておくと便利。
お好みに応じて、/e,を付加(自分的には遅くなる(特にネットワークアクセスが遅い時)ので付けないけど)。
explorer /e,`cygpath -d $*`
Eclipseのコンパイルエラー表示順
AがBを参照(例えば継承していたり)していて、Bにコンパイルエラーがあるため、Aもコンパイルエラーになっているような場合、本当はBのエラーを先に表示して欲しいのに、EclipseではAのエラーの方が先に出てくることがあって、正直、複数のファイルををいっぺんに変更するような場合には、使いものにならない感じだった。で、今ごろ気付いたのだけれど、Problemsのペインにはsort順序が付けられるようになっていて、デフォルトはこんな風になっていた。

ドロップダウンを見ると、ちゃんと"Creation Time"ってのがあるじゃん。

なんだよも〜。Eclipseってデフォルトの設定に変なのが多すぎ。これを見直せば毛嫌いする人が減ると思う。
発音
日本人が英語を話す時、日本のものでも、英語っぽいイントネーションで発音する。でも、日本語が流暢な欧米人が日本語を話しているのを聞くと、英語のものは英語の発音のままの場合が、ほとんどだと思う。これはどうしてなんだろうか。外国語教育のやり方の違いなんだろうか。なんとなく原語の発音を尊重する方がいいような気がする。今度から日本語は日本語の発音で話してみようか。
馬購入
馬というか、豹なのだけど、ようやく購入。

移動速度が、1.6倍。値段は免許代18Gで、豹が72G、しめて90G。金銭感覚的には、1G = $100なので、軽自動車購入って感じ。2倍速の虎はなんと900G、こんなの買えない。
雌の虎って、tigressっていうのだな。今日初めて知った。英語の知識って、かなりの部分、ゲームから仕入れているかも。
JSPの中でsessionという名前のローカル変数を宣言すると
Tomcat 4.1だとローカル変数の方が勝つ。WebSphere(5.1)だと、JSP暗黙オブジェクトの方が勝つ。エラーも何も出ないので、紛らわしい。まぁ普通はセッションクラスを自作することは無いから問題にはならないけど…
Request wrapper is not of type HttpServletRequestWrapper
WebSphereで、こんなエラーがログに残る時は、
[06/07/05 12:19:45:919 JST] 4c2c88bd WebGroup E SRVE0026E: [サーブレット・エラー]-[Request wrapper is not of type HttpServletRequestWrapper]: java.lang.IllegalStateException: Request wrapper is not of type HttpServletRequestWrapper
フィルタとかで、HttpServletRequestのラッパを作って、既存のリクエストをラップしている時に、HttpServletRequestWrapperを継承せずに、HttpServlerRequestをimplementsしただけのラッパクラスを自作しているのが原因。
符点
符点が1つついた音符は、50%長くなる。2つ付くと、50% + 25%で75%だ。符点n個ならば、1/2 + 1/4 + ... + 2^(-n)長くなる。これってなんか公式で簡単に計算できた気がするなぁ。なんだったっけ。
f(n) = 1/2 + 1/4 + ... + 1/2^nだから、
2 * f(n) = 1 + 1/2 + 1/4 + ... + 1/2^(n-1)
f(n) = 1/2 + 1/4 + ... + 1/2^(n-1) + 1/2^n
上から下を引くと、f(n) = 1 - 1/2^nか。とすると
len = len + (len - (len >> dot)));
で良さそうだ(dotは符点の数)。
引越し
再来週から、川崎に移動するらしい。近くなるけど、自分的には大崎の方が、まわりがきれいで好きかも。あと、人の歩く速度が違うよね。川崎って朝でも、みんなのんびり歩いていて、ちょっといらいらしてしまうことがある。
エスプレッソと天気
どうも天気の悪い日は、エスプレッソが濃い目に入る気がする。なぜなんだろう。多分気圧なんだろうけど、なんで気圧が低い方が濃くなるんだ?
気圧が低いと、湯が通り抜けるのに時間がかかる? いや、それはおかしい。そもそもポンプは周辺の気圧に対して+xx気圧のはずで、周りが0.9気圧なら10.9気圧、周りが1.1気圧なら11.1気圧みたいな感じになるはず。だから入口と出口の間の気圧差は、大差無いはず。
でも水の物性は絶対的な圧力で変わるから、圧力の絶対値が低い時の方が水の粘性が低くて、通り易かったりするのかな。いずれにせよ晴れた日は、ちょっときつ目に粉をつめるのが良いようだ。
ビブラート
「オーケストラからビブラートを取り覗く時だ」(ネタ元:いがぴょんの日記)
[引用はじめ]
「全然、ちっとも。ビブラートは、1830年代の特徴からは遙かに隔たったもので、それは欧米のオーケストラでは1930年代までは一般的ではなかったのです。」...
「20年代の初期には、流行に敏感でエンターテインメント志向のフランスの奏者たちが絶え間ないビブラートを試し始め、そして20年代後半にはイギリス人がその先例に倣いました。しかし、高潔なドイツや大きなアメリカの団体の大部分は、30年代になるまで手を染めませんでした。ベルリン・フィルは1935年まではっきりしたビブラートの録音は出てきませんし、ウィーン・フィルは1940年までありません。」...
「20世紀前半のバイオリン協奏曲の録音を聴くと、ソリストはビブラートを使っていますが、ドイツの最高のオーケストラはピュアな音色で演奏しています。当時はそれが普通だったのだと思われます。」
[引用おわり]
そうだったのか。しかし広まったということは、その方がウケが良かったんだろう。ビブラート無しってのも生で聴いてみたいけど、奏者は簡単に切り替えられるものなんだろうか。
JScrollPaneでのColumnHeader/RowHeaderのauto scroll
JScrollPaneには、ColumnHeaderが指定できる。これはスクロール粋の外側に配置されて、スクロール対象コンポーネントといっしょにスクロールする。ところが、このColumnHeader側をスクロールしてもなぜかスクロール対象コンポーネント側がスクロールしない。
public class NewJFrame extends javax.swing.JFrame {
/** Creates new form NewJFrame */
public NewJFrame() {
initComponents();
setBounds(0, 0, 600, 400);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
//
private void initComponents() {
jLabel2 = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
jLabel1 = new javax.swing.JLabel();
jLabel2.setText("WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD WORLD ");
jLabel2.setAutoscrolls(true);
jLabel2.setBorder(javax.swing.BorderFactory.createEtchedBorder());
jScrollPane1.setColumnHeaderView(jLabel2);
jLabel2.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(java.awt.event.MouseEvent evt) {
jLabel2MouseDragged(evt);
}
});
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setResizable(false);
jLabel1.setText("HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO ");
jLabel1.setAutoscrolls(true);
jLabel1.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(java.awt.event.MouseEvent evt) {
jLabel1MouseDragged(evt);
}
});
jScrollPane1.setViewportView(jLabel1);
getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER);
pack();
}//
private void jLabel2MouseDragged(java.awt.event.MouseEvent evt) {
jLabel2.scrollRectToVisible(new Rectangle(evt.getX(), evt.getY(), 1, 1));
}
private void jLabel1MouseDragged(java.awt.event.MouseEvent evt) {
jLabel1.scrollRectToVisible(new Rectangle(evt.getX(), evt.getY(), 1, 1));
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
// Variables declaration - do not modify
javax.swing.JLabel jLabel1;
javax.swing.JLabel jLabel2;
javax.swing.JScrollPane jScrollPane1;
// End of variables declaration
}
これを実行すると、こんな画面が出る。

HELLOのところでマウスのボタンを押し、そのままウィンドウの外にドラッグすると、ヘッダのWORLDもついてくる。

ところが、WORLDをドラッグしても、HELLOはついてこないのだ。

バグだよなぁと思いつつも、API仕様書には、この辺の動作について記述されていない。Bug paradeを見てみたら、あったあった。BUG ID:4202002。VOTEは自分を入れて30。って1999年から放置ですか。まぁスクロールコンポーネント側にイベントを委譲してやれば、回避できるから致命的ではないが、とりあえずバグとは認識しているようだ。
instanceofとgetClass()、どっちが速い?
クラスを実行時に判定したい時、instanceofとgetClass()があるけど、漠然とgetClass()の方が速いのではないかと思っていた。Stringのようにfinalだったら、instanceof StringもgetClass() == String.classもできることは、いっしょ(違いはinstanceofはnullの場合に黙ってfalseを返すことくらい)で、getClass()の方をなんとなく使っていた。実際のところどうなのかと思い、速度を測ってみた。
public class Foo0 {
static class Base {}
static class Foo extends Base {}
static class Bar extends Base {}
public static void main(String[] args) throws Exception {
Object obj1 = new Foo();
Object obj2 = new Bar();
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
dispatch1(obj1);
dispatch1(obj2);
}
System.out.println("instanceof Elapsed " + (System.currentTimeMillis() - start) + "ms.");
start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
dispatch2(obj1);
dispatch2(obj2);
}
System.out.println("getClass() Elapsed " + (System.currentTimeMillis() - start) + "ms.");
}
static int fooCount;
static int barCount;
public static void dispatch1(Object obj) {
if (obj instanceof Foo) {
++fooCount;
}
}
public static void dispatch2(Object obj) {
Class cls = obj.getClass();
if (cls == Foo.class) {
++fooCount;
}
}
}
Windows XP(SP2) PentiumM 1.8GHz 単位はms instanceof: Java 1.5.0_07: 100 (-target 1.4): 100 Java 1.6.0-b2: 90 getClass(): Java 1.5.0_07: 70 (-target 1.4): 210 Java 1.6.0-b2: 50
なんか、色々いじってて気づいたんだけど、なぜか-target 1.4つけると、getClass()が強烈に遅くなるんだよね。これはなぜなんだろう。それ以外の場合は思ったほどの違いは無い感じ。1.6が随分と速くなっているのに、ちょっと驚き。もうひとつ不可解な現象があって、判定クラス数を増やすと、
public static void dispatch1(Object obj) {
if (obj instanceof Foo) {
++fooCount;
}
else if (obj instanceof Bar) {
++ barCount;
}
}
public static void dispatch2(Object obj) {
Class cls = obj.getClass();
if (cls == Foo.class) {
++fooCount;
}
else if (cls == Bar.class) {
++barCount;
}
}
instanceof: Java 1.5.0_07: 180 (-target 1.4): 180 Java 1.6.0-b2: 120 getClass(): Java 1.5.0_07: 280 (-target 1.4): 260 Java 1.6.0-b2: 180
なんか、getClass()が異様に遅くなる。判定クラス数が2倍になっただけなのに、4倍近く遅くなるのだ。なんでこんなことになるんだろう。謎だ。いやそれにしても1.6は優秀だ。Javaって、まだまだ最適化の余地があるんだな。とりあえず比較の数が多いなら、instanceofの方が速い感じ。でも1.6以降なら大差無しって感じかな。








