oinume journal

Scratchpad of what I learned

Jettyでシンボリックリンクを有効にする

最近Jettyと戯れている。Tomcatに比べてちょっと扱いづらいところが可愛らしい。

Jettyはセキュリティ的な理由でAlias(Symbolic Link)をデフォルトでは無効にしている。でもsymlink使いたいんですよあたしゃ。というわけで、調べた結果web.xmlに下記を追加するのが一番楽そうなのでこれを設定したらいけた。

web.xml

<servlet>
    <servlet-name>jetty</servlet-name>
    <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
    <init-param>
        <param-name>aliases</param-name>
        <param-value>true</param-value>
   </init-param>
   <load-on-startup>0</load-on-startup>
</servlet>

JVMのオススメ起動オプション

なんか秘伝のタレみたいになってきたので後世のために共有。

前提

  • Webアプリケーションを想定
  • TomcatなりJettyなりがListenするポートは外部からはアクセスできない

※-Xms -Xmx -Xmn あたりは搭載しているメモリ容量によって変える、-XX:MaxPermSize -XX:PermSizeは384mあれば十分だと思うけどロードするクラスの数次第なので要調整。

NOW=`date "+%Y%m%d-%H%M%S"`

JAVA_OPTS="-server -Xms2g -Xmx2g -Xmn1g -XX:MaxPermSize=384m -XX:PermSize=384m \

-XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=85 -XX:MaxTenuringThreshold=32 \

 

Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング

Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング

 

 

-XX:+PrintClassHistogram -XX:+HeapDumpOnOutOfMemoryError -XX:+UseConcMarkSweepGC \

-XX:+CMSParallelRemarkEnabled -XX:+UseParNewGC \

-verbose:gc -Xloggc:/usr/local/tomcat/logs/gc.log.$NOW \

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps \

-Dcom.sun.management.jmxremote \

-Dcom.sun.management.jmxremote.port=5400 \

-Dcom.sun.management.jmxremote.authenticate=false \

-Dcom.sun.management.jmxremote.ssl=false"

読んでおくべきページ

追記:GCログのローテーション

@n0tsさんから

と言われて最初はローテーションできないと思っていたんだけど、最近のJVMだと下記オプションでGCのログがローテートできるらしい。

-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=10M

↑はローテーション数を10、1ファイルあたり10MBにする設定。Java 1.6.0_34、1.7.0_02以上から利用できるオプションらしい。ちなみにUbuntu 12.04のOpenJDK 1.7.0_15でもこのオプションは使えた。

上記を加えた秘伝のタレJVMオプションは下記になる。

JAVA_OPTS="-server -Xms2g -Xmx2g -Xmn1g -XX:MaxPermSize=384m -XX:PermSize=384m \
-XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=85 -XX:MaxTenuringThreshold=32 \ 
-XX:+PrintClassHistogram -XX:+HeapDumpOnOutOfMemoryError -XX:+UseConcMarkSweepGC \ 
-XX:+CMSParallelRemarkEnabled -XX:+UseParNewGC \
-verbose:gc -Xloggc:/usr/local/tomcat/logs/gc.log \
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=10M \
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=5400 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false"

結論

JVMはマイナーバージョンアップでもオプションが増えたりするので油断できない。

Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング

Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング

 

 

JVMのGCのログを-XX:+PrintGCDateStampsでhuman readableにする

English version

 

JVMGCのログ出すじゃないですか。んで、その時↓みたいに -XX:+PrintGCTimeStamps っていうオプションを指定するじゃないですか。

 

TODAY=`date "+%Y%m%d-%H%M%S"`

 

JAVA_OPTS="-server -Xms512m -Xmx512m -Xmn256m -XX:PermSize=256m -XX:MaxPermSize=256m \

-XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseParNewGC \

-XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=32 -XX:TargetSurvivorRatio=85 \

-verbose:gc -Xloggc:/usr/local/tomcat/logs/gc.log.$TODAY \

-XX:+PrintGCDetails \

-XX:+PrintGCTimeStamps \

-DJENKINS_HOME=/var/lib/jenkins \

"

 

 

でもこれ、悲しいかな、JVM起動時の時間からの相対時間なんすよね... いやJVM起動した時間なんか調べるの面倒だし、いちいち相対時間から計算するの面倒だし、っていうあなたに -XX:+PrintGCDateStamps をオススメします。これを指定するとGCのログが絶対時間で出力される。(PrintGCDateStampsはJava6 u4から利用可能)

 

びふぉー(-XX:+PrintGCTimeStamps)

 

63.156: [CMS-concurrent-mark-start]

64.175: [CMS-concurrent-mark: 0.844/1.019 secs] [Times: user=2.59 sys=0.21, real=1.02 secs]

64.176: [CMS-concurrent-preclean-start]

64.197: [CMS-concurrent-preclean: 0.019/0.021 secs] [Times: user=0.05 sys=0.01, real=0.03 secs]

64.197: [CMS-concurrent-abortable-preclean-start]

CMS: abort preclean due to time 69.246: [CMS-concurrent-abortable-preclean: 4.756/5.049 secs] [Times: user=12.37 sys=0.75, real=5.04 secs]

69.248: [GC[YG occupancy: 235103 K (235968 K)]69.248: [Rescan (parallel) , 0.2130820 secs]69.461: [weak refs processing, 0.0017410 secs] [1 CMS-remark: 138712K(262144K)] 373816K(498112K), 0.2149850 secs] [Times: user=0.61 sys=0.00, real=0.21 secs]

69.466: [CMS-concurrent-sweep-start]

69.468: [GC 69.468: [ParNew: 235968K->26176K(235968K), 0.0955880 secs] 374601K->174167K(498112K), 0.0957410 secs]

 

 

あふたー(-XX:+PrintGCDateStamps)

 

2013-01-20T13:20:43.395+0900: 0.847: [Full GC (System) 0.847: [CMS: 0K->1757K(262144K), 0.0502300 secs] 16808K->1757K(498112K), [CMS Perm : 10054K->10045K(262144K)], 0.0503520 secs] [Times: user=0.09 sys=0.00, real=0.05 secs]

2013-01-20T13:20:47.952+0900: 5.404: [GC 5.404: [ParNew: 209792K->1976K(235968K), 0.0304360 secs] 211549K->3733K(498112K), 0.0305290 secs] [Times: user=0.05 sys=0.00, real=0.03 secs]

2013-01-20T13:20:53.692+0900: 11.144: [GC 11.144: [ParNew: 211768K->3713K(235968K), 0.0192760 secs] 213525K->5470K(498112K), 0.0193720 secs] [Times: user=0.04 sys=0.00, real=0.02 secs]

2013-01-20T13:21:02.932+0900: 20.383: [GC 20.383: [ParNew: 213505K->21672K(235968K), 0.0812540 secs] 215262K->23429K(498112K), 0.0813650 secs] [Times: user=0.13 sys=0.03, real=0.08 secs]

2013-01-20T13:21:07.448+0900: 24.899: [GC 24.900: [ParNew: 231464K->26176K(235968K), 0.1677760 secs] 233221K->38711K(498112K), 0.1678730 secs] [Times: user=0.32 sys=0.05, real=0.17 secs]

2013-01-20T13:21:12.021+0900: 29.473: [GC 29.473: [ParNew: 235968K->26176K(235968K), 0.2173720 secs] 248503K->57677K(498112K), 0.2175440 secs] [Times: user=0.62 sys=0.00, real=0.22 secs]

2013-01-20T13:21:16.168+0900: 33.620: [GC 33.620: [ParNew: 235968K->26176K(235968K), 0.0859270 secs] 267469K->59042K(498112K), 0.0860640 secs]

 

 

これで人間が読めるGCのログになりましたね。

 

[tmkm-amazon]4873113881[/tmkm-amazon]

commons-langのEqualsBuilderの速度

Javaの有名なライブラリでcommons-langというのがある。この中にEqualsBuilderというequals()メソッドを簡単に実装するための便利クラスがある(HashCodeBuilder, ToStringBuilderというクラスもある)。使い方はこんな感じ。

 

@Override

public boolean equals(Object o) {

if (this == o) {

return true;

} else if (!(o instanceof MyClass)) {

return false;

}

MyClass other = (MyClass)o;

return new EqualsBuilder()

.append(this.field1, o.field1)

.append(this.field2, o.field2)

.build();

}

 

便利なので愛用してるんだけど、普通にequals()を実装した場合と比べてパフォーマンス的にどうなの?っていうのが気になったのでベンチマークを取ってみた。ちなみに、EqualsBuilder.reflectionEquals()というリフレクションを使うバージョンもあるので、あわせて調べてみた。

 

結果

下記の3パターンで10万回equals()を呼び出してベンチした結果(ベンチマークのコード

 

実装かかった時間(ミリ秒)

EqualsBuilderを使わない普通の実装44

EqualsBuilderを使った実装50

EqualsBuilder.reflectionEqualsを使った実装807

 

 

結論としては

 

 

  • EqualsBuilderを使わない場合と使った場合でほとんど差がないのでEqualsBuilderは積極的に使うべし

 

 

  • 当たり前だけどリフレクションを使うと遅い。ただ、10万回呼び出して807ミリ秒なので、ケース・バイ・ケースで使えばいいと思う。

 

 

になるのかなぁと。reflectionEqualsは1行でequals()の処理がかけてしまい麻薬的に便利なので、使えるところでは使ってもいいんじゃないかと思った次第。

 

[tmkm-amazon]489471499X[/tmkm-amazon]

FreeMarkerで余計な改行が入らないようにする

JavaのテンプレートエンジンとしてFreeMarkerがある。テンプレートエンジンって割とみたいに書くことで余計な改行が入らないようになるじゃないですか。このFreeMarkerではどうするの?っていうのを同僚の人に教えてもらったので、ググれるようにまとめておく。

はじめに

やりたかったことは、DBのテーブル情報から下記のようなクラスを生成するためのテンプレートで(1)と(2)のところに改行を入れたくなかった、ということ。

最初のFreeMarkerのテンプレートファイルはこんな感じ。

改行を入れたくないところにコメントを入れることで回避

結論からいうと、FreeMarkerのコメントのタグ

<#-- -->

を改行を入れたくない部分に入れたら改行を削除することができた。

FreeMarkerの空白を削除するタグ

ちなみに、いろいろ調べてみてFreeMarkerには空白を削除するタグが幾つかあることがわかったが、改行だけでなく半角スペースやタブなども削除してしまうので今回のケースでは使えなかった。

最後に

HTMLを出力する場合はあまり改行は気にならないかもしれなけど、今回のようにJavaのソースとかを生成する場合は改行の制御がちゃんとできるテンプレートエンジンじゃないと辛いなぁと思った。結果としてやりたいことは実現できたから良かったけど。

 

現場で使えるJavaライブラリ

現場で使えるJavaライブラリ