oinume journal

Scratchpad of what I learned

会社の本番環境をJava8で動かそうとしたらNewRelicでエラーが出た

そろそろJava8でコード書きたい
 ↓
まずはJVMをJava8で動かさないと...!(JVMに問題があった時に詰む)
 ↓
本番のJVMをJava8にした
 ↓
起動時にNewRelic入れてるサーバでエラーが出た
 ↓
対応に数ヶ月かかるかもしれないとのこと\(^o^)/ (←イマココ)

出たエラーはこんな感じ。

Apr 15, 2014 13:56:01 +0900 [13004 1] com.newrelic INFO: Agent is using Logback
Apr 15, 2014 13:56:02 +0900 [13004 1] com.newrelic INFO: Loading configuration file "/path/to/newrelic/newrelic.yml"
. . . java.lang.IllegalArgumentException
    at com.newrelic.deps.org.objectweb.asm.ClassReader.<init>(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.ClassReader.<init>(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.ClassReader.<init>(Unknown Source)
    at com.newrelic.agent.util.asm.PatchedClassWriter.getClassReader(PatchedClassWriter.java:102)
    at com.newrelic.agent.util.asm.PatchedClassWriter.getCommonSuperClass(PatchedClassWriter.java:77)
    at com.newrelic.deps.org.objectweb.asm.ClassWriter.a(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.Frame.a(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.Frame.a(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.MethodWriter.visitMaxs(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.tree.MethodNode.accept(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.commons.JSRInlinerAdapter.visitEnd(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.MethodVisitor.visitEnd(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.ClassReader.b(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.ClassReader.accept(Unknown Source)
    at com.newrelic.deps.org.objectweb.asm.ClassReader.accept(Unknown Source)
    at com.newrelic.agent.instrumentation.context.InstrumentationContextManager$3.getFinalTransformation(InstrumentationContextManager.java:315)
    at com.newrelic.agent.instrumentation.context.InstrumentationContextManager$3.transform(InstrumentationContextManager.java:256)
    at com.newrelic.agent.instrumentation.context.InstrumentationContextManager$2.transform(InstrumentationContextManager.java:192)
    at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:455)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:367)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)

Java逆引きレシピ

Java逆引きレシピ

JVM Operation Casual Talks #1でLTとパネルディスカッションしてきた

JVM Operation Casual Talks #1でLTとパネルディスカッションしてきました(togetterまとめ)。

この日登壇した人でJVM好きな人っていたんだろうか?っていうぐらいLL寄りな人が多かった印象だった。パネルディスカッションというものは初めてだったんだけど(*1)、人の目の前でJVMをDISれてよかったなぁと思う。はてなさんの新しいサービスはScalaでできているらしくてそれがすごいビックリした。(LLで頑張る会社だと思ってたので)

パネルディスカッション中に「JVMのプロセスはカジュアルに再起動するものじゃない」という意見が出て、理想はそうなんだろうけど現実はFullGCしたりするしじゃじゃ馬なんだよなぁと思ったりした。結局JVMの上に乗るものは人間が作るものでバグがあったり不完全だったりするので、危なくなったらLBから切り離して再起動する運用は全然アリだと思う。もちろん進んでやるものではないですが。

飲み会の場で第2回があるとしたらウチの会社でやることになってたので、誰か気が向いたら開催されるとおもいます。

1.@oranieと@waysakuとじゃんけんして負けたので出ることになった

ガベージコレクションのアルゴリズムと実装

ガベージコレクションのアルゴリズムと実装

Make Javas VM's GC log human readable with -XX:+PrintGCDateStamps

I use Java VM's option -XX:+PrintGCTimeStamps in order to output time in GC log of Java VM , Just like this.

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 \
"

But, the output is relative time from starting time of Java VM. So it's difficult to read. However, we have an another option -XX:+PrintGCDateStamps !! It is an option to output absolute time!! (Available from Java6 u6)

-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-&gt;26176K(235968K), 0.0955880 secs] 374601K-&gt;174167K(498112K), 0.0957410 secs]

-XX:+PrintGCDateStamps

2013-01-20T13:20:43.395+0900: 0.847: [Full GC (System) 0.847: [CMS: 0K-&gt;1757K(262144K), 0.0502300 secs] 16808K-&gt;1757K(498112K), [CMS Perm : 10054K-&gt;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-&gt;1976K(235968K), 0.0304360 secs] 211549K-&gt;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-&gt;3713K(235968K), 0.0192760 secs] 213525K-&gt;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-&gt;21672K(235968K), 0.0812540 secs] 215262K-&gt;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-&gt;26176K(235968K), 0.1677760 secs] 233221K-&gt;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-&gt;26176K(235968K), 0.2173720 secs] 248503K-&gt;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-&gt;26176K(235968K), 0.0859270 secs] 267469K-&gt;59042K(498112K), 0.0860640 secs]

-XX:+PrintGCDateStamps is a great option for humans.

Effective Java (Java Series)

Effective Java (Java Series)

MacでOracle JDKをインストールする

最近MacJDKをインストールすることが多いんだけど(脆弱性がよく見つかる)、インストールされるディレクトリがどこだか覚えられらなくなってきたのメモ。

インストール

Oracle からMac版の jdk-7uXX-macosx-x64.dmg のファイルを選んでダウンロードする。んで、ダウンロードしてきたdmgファイルからインストーラーの指示に従いインストールする。

インストールされる場所

/Library/Java/JavaVirtualMachines/jdk1.7.0_XX.jdk/ にインストールされる。EclipseIntelliJ IDEAのJVMを指定する場合は、 /Library/Java/JavaVirtualMachines/jdk1.7.0_XX.jdk/Contents/Home というディレクトリを指定すればよい。

f:id:oinume:20130623165835p:plain

個人的にやってること

自分は最新版のJDKが出るたびに入れ替えているんだけど、コマンドラインからも最新のJVMを使えるように

ln -s /Library/Java/JavaVirtualMachines/jdk1.7.0_XX.jdk/Contents/Home /usr/local/java

しておいて、~/.bashrc などで

if [ -d /usr/local/java ]; then
    export JAVA_HOME=/usr/local/java
    export PATH=$JAVA_HOME/bin:$PATH
fi

というようにPATHをセットしている。これでsymlinkさえ張り替えれば最新のJVMを使えるようにしている。

余談

Java逆引きレシピ

Java逆引きレシピ

JRebelを使ってJava開発時のサーバの再起動をなくす

JRebelというソフトウェアを使うと、JavaでWebアプリケーションを開発する時に、ソースの修正→サーバ再起動 or 再ディプロイ がいらなくなるよーという話。

ライセンス

商用で使う場合はちゃんとライセンスを購入する必要があるけど、Socialという謎ライセンスだとフリーで使えるみたい。(ちゃんと理解してない)

myJRebel Social is a FREE version of myJRebel that is meant for anyone working on non-commercial projects, who is tired of building and redeploying Java code.

インストール

Eclipseを使っていれば Help -> Eclipse Market Place をクリックして jrebel と検索するとプラグインがインストールできるのでインストールして終わり。再起動すると設定画面が出るので、myJRebelのサイトに登録して、指示通りにSocialのPlanを選択してAtivateするとライセンスコードが表示されるので、それをEclipseの画面でコピペすればOK。

今回試した時の環境

使ってみる

自分はサーバの起動にはMavenのjetty:runを使っているので、その起動の設定画面で下記のようにJRebelタブをクリックして"Enable JRebel Agent"をクリックすればOK。

f:id:oinume:20130608131554p:plain

あとはEclipseで Project -> Build Automatically にチェックが入っているかを確認。これが入っていないとソースを更新してもコンパイルされないのであまり恩恵が受けられない。

この状態でサーバを起動するとConsoleに下記のように表示される。

2013-06-08 12:54:14 JRebel: #############################################################
2013-06-08 12:54:14 JRebel: 
2013-06-08 12:54:14 JRebel:  JRebel 5.3.0 (201305281348)
2013-06-08 12:54:14 JRebel:  (c) Copyright ZeroTurnaround OU, Estonia, Tartu.
2013-06-08 12:54:14 JRebel: 
2013-06-08 12:54:14 JRebel:  Over the last 1 days JRebel prevented 
2013-06-08 12:54:14 JRebel:  at least 1 redeploys/restarts saving you about 0 hours.
2013-06-08 12:54:14 JRebel: 
2013-06-08 12:54:14 JRebel:  This product is licensed to Kazuhiro Oinuma
2013-06-08 12:54:14 JRebel: 
2013-06-08 12:54:14 JRebel:  License acquired through myJRebel server.
2013-06-08 12:54:14 JRebel: 
2013-06-08 12:54:14 JRebel:  You are subscribed for the plan "JRebel Social Plan",
2013-06-08 12:54:14 JRebel:  subscription is for lifetime.
2013-06-08 12:54:14 JRebel:  next license check with the server is required by 2013-06-09.

あとは実際にControllerなどのソースコードをいじってみて修正が反映されているかを確認してみよう。自分はこんな感じのコードで model.addAttribute("message", "hello world"); の部分を修正して試してみた。

IndexController

package net.lampetty.samples.spring.mvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {

    @RequestMapping(value = "/")
    public String index(Model model) {
        model.addAttribute("message", "hello world");
        return "/index";
    }
}

index.ftl

<html>
<head>
<title>index</title>
</head>
<body>
${message!"Default message"}
</body>
</html>

Ecipse 3.7 Indigoを使っている場合

どうもEclipse 3.7だとJRebelのプラグインの最新のバージョンがインストール出来ないせいかうまく動かなかったので、Standalone版をインストールしたところうまく動いた。

JRebelのサイトからダウンロードして ~/jrebel に解凍。んで ~/jrebel/bin/jrebel-config.jar をダブルクリックするとライセンスを入力する画面が出てくるのでアクティベートしておく。あとはEclipseから jetty:run でサーバを起動するときのJVMのオプションに ~/jrebel/jrebel.jar のフルパスを書いておくことでちゃんと使えるようになる。

-javaagent:/Users/kazuhiro/jrebel.jar -Xms256m -Xmx512m -XX:MaxPermSize=256m

詳しいことは公式ドキュメントを見てもらえればと。

まとめ

JRebel でディプロイする時間を節約してビーチにでも行こうぜ!

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

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