放課後プログラミング

調べたことや考えたことなどを忘れないために書きます。

シェル芸勉強会にUst参加しました

会社の同期に誘われてシェルの勉強会に遠隔参加してみました。

以下が勉強会で使われた資料です。

ツイッター実況
https://twitter.com/hashtag/%E3%82%B7%E3%82%A7%E3%83%AB%E8%8A%B8

前半戦

Q1

・次のechoの出力を、echoにパイプをつなげて足し算してください。

$ echo -12,135,123 135,123
-12,135,123 135,123

以下のように解きました

$ echo -12,135,123 135,123 | sed -e "s/[ ,]/\n/g" | awk '{sum+=$0} END{print sum}'
504

問題を勘違いしてて-12135123 + 135123という意味でした

ツイッターに流れた解答

sedではなくtrで十分でした
これも知らなかったです↓

$ echo a b | awk ''
$ echo a b | awk '1'
a b
$ echo a b | awk '2'
a b
$ echo a b | awk '$0'
a b
$ echo a b | awk '$1'
a b
$ echo a b | awk '$2'
a b
$ echo a b | awk '$3'
$ echo a b | awk '{}'
$ echo a b | awk '{$0}'
$ echo a b | awk '{}1'
a b
$ echo a b | awk '{print $0}1'
a b
a b

perlで足し算してる方もいました、単純な計算をする方法はたくさんありそう

Q2

・次のメモについて、各レコードが「名前 点数」の順になるようにデータを整形しましょう。

$ cat score
45 鎌田
濱田 72
今泉 84
24 上田
94 斉藤

以下のように解きました

$ cat score | awk '/^[0-9]/{print $2,$1 ; next} {print $0}'
鎌田 45
濱田 72
今泉 84
上田 24
斉藤 94

ツイッターに流れた解答

代入使って出力場所一箇所にできるのは思いつかなかったです

awkでしていた置換sedで十分でした

Q3.

・m/sに直してください
 ・1マイル = 1.609km で演算を

$ cat speed
100km/h
16mph

以下のように解きました

$ cat speed | sed -e "s/^\([0-9]*\)/\1 /g" | awk '{ if($2=="km/h"){res = $1*1000/3600;printf "%sm/s\n",res} if($2=="mph"){res = $1*1.609*1000/3600;printf "%sm/s\n",res}}'
27.7778m/s
7.15111m/s

ツイッターに流れた解答

数値を抽出して計算するときとても便利そうなTips

$ cat speed | awk '{print $1}'
100km/h
16mph
$ cat speed | awk '{print $1*1}'
100
16
$ echo 1a2 | awk '{print $1*1}'
1

2番目の数値は見えないみたいです

sedとbcを組み合わせるとゴミの混ざった文字列からいろいろ計算できそうです
bcコマンドに渡す計算式の先頭にscale=4;を付けておくと小数点以下4桁までで丸めてくれるらしい

$ cat speed | sed 's/km/*1000/;s@mp@*1609/@;s/h/3600/;' | bc
27
7
$ cat speed | sed 's/km/*1000/;s@mp@*1609/@;s/h/3600/;' | sed 's/^/scale=4;/' | bc
27.7777
7.1511

Q4.

さいとうさん、さわださん、ひろたさん、いとうさんの数を数えてみてください。

$ cat name
斎藤 斉藤 沢田 澤田 伊藤
齋藤 齊藤 広田 廣田

以下のように解答しました

$ cat name | sed "s/ /\n/g" | awk '/[斉斎齊齋]藤/{saito++} /[沢澤]田/{sawada++} /[広廣]田/{hirota++} /伊藤/{itou++ } END{printf "saito\t%s\nsawada\t%s\nhirota\t%s\nitou\t%s",saito,sawada,hirota,itou}'
saito   4
sawada  2
hirota  2
itou    1

ツイッターに流れた解答

表記の振れをsedで解消してからシンプルに数えても同じようにできました

kakasiという形態素解析エンジンを使う解ありました
http://ja.wikipedia.org/wiki/KAKASI
表記の振れのパターンが多い時はこうすれば簡単そうです

後半戦

Q5.

・次のCSVに書いてある数字を足し算してください。

$ cat csv
1,2,"123,456",-5,"-123,444"
6,7,8,"12",9

以下のように解きました

$ cat csv | sed -e "s/\"\(-*[0-9]*\),*\([0-9]*\)\"/\1\2/g" | sed "s/,/\n/g" | awk '{sum+=$0}END{print sum}'
52

ツイッターに流れた解答

xargsというコマンドは主にfindコマンドの結果を別のコマンドの引数に並べるために使われる

$ touch a.txt b.txt c.txt
$ find . -name "*.txt" -type f
./a.txt
./b.txt
./c.txt
$ find . -name "*.txt" -type f | xargs
./a.txt ./b.txt ./c.txt
$ find . -name "*.txt" -type f | xargs rm
$ find . -name "*.txt" -type f
$

-n1オプションで逆の処理が行える

$ touch a.txt b.txt c.txt
$ find . -name "*.txt" -type f
./a.txt
./b.txt
./c.txt
$ find . -name "*.txt" -type f | xargs
./a.txt ./b.txt ./c.txt
$ find . -name "*.txt" -type f | xargs | xargs -n1
./a.txt
./b.txt
./c.txt

このとき、"'で囲ってると1つの要素としてカウントしてくれることを利用しているようです。

$ echo \"a b\" c
"a b" c
$ echo \"a b\" c | xargs -n1
a b
c

Q6.

・次のデータを行列として転置してください。

$ cat matrix
a b c
d e f
g h i
###出力###
$ ???
a d g
b e h
c f i

解き方わからず。

ツイッターに流れた解答

配列の中身の遷移は

a[1] <= " a"
a[2] <= " b"
a[3] <= " c"
a[1] <= " a d"
a[2] <= " b e"
a[3] <= " c f"
a[1] <= " a d g"
a[2] <= " b e h"
a[3] <= " c f i"
a[1] <= "a d g"
a[2] <= "b e h"
a[3] <= "c f i"

$1=$1をすると$0の各要素の間にある余計なスペースは削されるみたいです

$ echo " a  b " | awk '1' | od -a
0000000  sp   a  sp  sp   b  sp  nl
0000007
$ echo " a  b " | awk '$1=$1' | od -a
0000000   a  sp   b  nl
0000004

調べたら下記URLの項目27にこの使い方が載っていました。
http://www.catonmat.net/blog/awk-one-liners-explained-part-two/

To remove whitespace between fields you may use this one-liner:

awk '{ $1=$1; print }'

This is a pretty tricky one-liner. It seems to do nothing, right? Assign $1 to $1. But no, when you change a field, Awk rebuilds the $0 variable. It takes all the fields and concats them, separated by OFS (single space by default). All the whitespace between fields is gone.

Q7.

・次のIPv6IPアドレスから、省略された0を復元してください。
 ・4桁の頭のゼロは省略できる

$ echo 2001:db8:20:3:1000:20:3
2001:db8:20:3:1000:20:3

以下のように解きました

$ echo 2001:db8:20:3:1000:20:3 | sed "s/:/\n/g" | awk '{while(length($0)<4){$0 = "0" $0} if(NR!=1){printf ":"} printf "%s",$0}'
2001:0db8:0020:0003:1000:0020:0003

ツイッターに流れた解答

0を補充してから後方4文字を抜き出す方法で、length()を見ながら足していくより高速にできそうです
-I@@を元の文字として続く処理の出力で置換することができる。

$ echo 1:2 | xargs -d:
1 2

$ echo 1:2 | xargs -d: | od -a
0000000   1  sp   2  nl  nl
0000005
$ echo 1:2 | xargs -d: -I@ echo A@A
A1A
A2
A
$ echo 1:2 | xargs -d: -I@ echo A@A | od -a
0000000   A   1   A  nl   A   2  nl   A  nl
0000011
# -dオプションをつけると改行も普通の入力文字として処理するので
# 最初のechoの改行によって意図しない場所で改行される
$ echo -n 1:2 | xargs -d: -I@ echo A@A
A1A
A2A
$ echo -n 1:2 | xargs -d: -I@ echo A@A | od -a
0000000   A   1   A  nl   A   2   A  nl
0000010

sedも難しいので分解します

$ cat test
hogepiyo
fugamoge
foobar

# sedはデフォルトで入力された全てを処理が終わったら出力します
$ cat test | sed ""
hogepiyo
fugamoge
foobar

# pはマッチした行を出力します
$ cat test | sed "1p"
hogepiyo
hogepiyo
fugamoge
foobar

# -nでデフォルトの出力を消せます
$ cat test | sed -n "1p"
hogepiyo

# このようにしてhogeのある行にマッチさせられます
$ cat test | sed -n "/hoge/p"
hogepiyo

# /./は空白ではない行にマッチします
$ cat test | sed -n "/./p"
hogepiyo
fugamoge
foobar

# 置換して後方4文字だけを残します
$ cat test | sed "s/.*\(....\)$/\1/"
piyo
moge
obar

# 空白でない行に対して後方4文字を残し、それらの該当行のみ出力します
$ cat test | sed -n '/./s/.*\(....\)$/\1/p'
piyo
moge
obar

僕の環境(Cygwin, GNU sed 4.2.2)では各sed置換の間をスペースではなくセミコロンにしたら動きました
こちらの解法も0を補充してから後方4文字を抽出していますが、処理中で各フィールドを分割していない点が異なっていました

Q8.

・次のIPv6IPアドレスから、省略された0を復元してください。
 ・4桁の頭のゼロは省略できる
 ・1回だけ、:0000:0000:と0000が続くところは::と省略できる。

$ cat ipv6
2001:db8::1234:0:0:9abc
2001:db8:20:3:1000:100:20:3
2001:db8::9abc

解き方わからず。

ツイッターに流れた解答

awk -F:は区切り文字を:に指定します。
IPv6アドレスは省略しない場合8フィールドあるので、8-NF:::0::に置換してから:::に置換し、最後にQ7と同じ処理をして解かれています。

おわり

知らなかったコマンドが普通に使われていたりで僕にとっては高度な内容でしたが、慣れ始めていたawkをいろいろいじれたり他の方の解答も見れたりで多く学ぶことができました。
主催者の方と実況者の方ありがとうございました。

※もし都合の悪い引用があればご連絡ください

Sublime Textの導入

Sublime Text Build 3059

Sublime Textを入れたので、最初に行った設定をメモしておきます。

Package Controlのインストール

いろんな機能拡張パッケージを管理するパッケージをインストールする

https://sublime.wbond.net/installation

1) View > Show Consoleでコンソールを表示
2) 上記サイトに載っているコマンドを叩く

IMEをインライン表示にする

デフォルトだとIMEがウィンドウの端に表示されて使いづらい
1) Ctrl + Shift + P
2) Package Control: Install Package
3) IMESupport
不完全な感じではあるけどウィンドウの端に表示されるよりはだいぶ使いやすくなる

Font Sizeの変更

個人的にデフォルトだと小さすぎるので変更する
1) Preferences > Settings - User
2) json"font_size":16を追加する
セーブしてうまくいけば再起動などせずに即時反映。jsonが正しくパースできないとダイアログでそう教えてくれる。

他にもたくさん設定項目があるので
Preferences > Settings - Default
を見て、変更したいパラメータをSettings - Userに書いて設定を上書きしていけばいい

OracleDBで任意の日付データセットを得る

SQLのクエリで日付のデータセットを得たいときは以下のようなSELECT文を発行すればいい。

SELECT SYSDATE + ROWNUM - 1 FROM ALL_CATALOG WHERE ROWNUM < 3;

June, 05 2014 15:39:59+0000
June, 06 2014 15:39:59+0000
ROWNUM
SELECTの結果セットの各レコードについて1から始まる昇順の数値が割り当てられる。
ALL_CATALOG
現行のユーザがアクセス可能な全ての表、クラスタ、ビュー、シノニムおよび順序を示す。ある程度の行数の結果セットを得る際に、その行数確保のために便宜上FROM句に用いられることがある。1行の結果がほしい際にはDUALが用いられることが多い。
サンプル

今日から明後日までの日付を時間を切り捨てて得る

SELECT TRUNC(SYSDATE + ROWNUM - 1) FROM ALL_CATALOG WHERE ROWNUM < 4;

June, 05 2014 00:00:00+0000
June, 06 2014 00:00:00+0000
June, 07 2014 00:00:00+0000

先月の最初の3日を得る

SELECT TRUNC(ADD_MONTHS(SYSDATE, -1), 'MM') + ROWNUM - 1 FROM ALL_CATALOG WHERE ROWNUM < 4;

May, 01 2014 00:00:00+0000
May, 02 2014 00:00:00+0000
May, 03 2014 00:00:00+0000

今年の各月の最終日を得る

SELECT LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), ROWNUM-1)) FROM ALL_CATALOG WHERE ROWNUM < 13;

January, 31 2014 00:00:00+0000
February, 28 2014 00:00:00+0000
March, 31 2014 00:00:00+0000
April, 30 2014 00:00:00+0000
May, 31 2014 00:00:00+0000
June, 30 2014 00:00:00+0000
July, 31 2014 00:00:00+0000
August, 31 2014 00:00:00+0000
September, 30 2014 00:00:00+0000
October, 31 2014 00:00:00+0000
November, 30 2014 00:00:00+0000
December, 31 2014 00:00:00+0000

今年の各月の最終日の日付の数値を得る

SELECT TO_NUMBER(TO_CHAR(LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), ROWNUM-1)), 'DD')) FROM ALL_CATALOG WHERE ROWNUM < 13;

31
28
31
30
31
30
31
31
30
31
30
31

上記の結果はいずれも「SQL Fiddle」で実行したもの

http://sqlfiddle.com/

ヒープ領域のチューニング

前回の投稿ガベージコレクションの仕組みの続きです。

GCにかかる時間はアプリケーション実行時間の10~15%以下程度に収まるのが良いと言われている。GCにそれ以上の時間がかかっていて、それがボトルネックになっている場合はチューニングを行ったほうが良い。

各種ヒープの設定

JVMの起動オプションに指定することでヒープのサイズ等を設定することができる。
(前回からの引用)

オプション 説明
-Xms? ヒープの初期サイズ、続けて512kのように書いて指定する。 -Xms128m
-Xmx? ヒープの最大サイズ、続けて128mのように書いて指定する。 -Xms512m
-XX:NewRatio=? young世代とtenured世代のサイズ比率、自然数で指定する。NewRatio=Tenured/Young。 -XX:NewRatio=4
-XX:SurvivorRatio=? Eden領域とSurvivor領域のサイズ比率、自然数で指定する。SurvivorRatio=Eden/2Survivor。 -XX:SurvivorRatio=6
-XX:MaxTenuringThreshold=? 殿堂入り閾値自然数で指定する。 -XX:MaxTenuringThreshold=32

チューニングに用いるツール

jdkに標準でチューニングに使えるツールが付属している。
パスは
/Path/To/JDK_install/bin/
僕の場合はwindowsなので
C:\Program Files\Java\jdk1.8.0\bin\
でした。

JavaプロセスのプロセスIDの取得

調べたいアプリケーションのプロセスIDをまずは取得しないと何も始まらない

$ jps
$ jps -lvm

下のオプションをつけるとより詳細に情報が出力される。たとえば僕のIntelliJは以下のようになっていた

$ jps -lvm
4924  -Xms128m -Xmx512m -XX:MaxPermSize=250m -XX:ReservedCodeCacheSize=64m -ea -Dsun.io.useCanonCaches=false -Djava.net.preferIPv4Stack=true -Djsse.enableSNIExtension=false -XX:+UseCodeCacheFlushing -XX:+UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB=50 -Xbootclasspath/a:C:\Program Files (x86)\JetBrains\IntelliJ IDEA 13.1.1\lib\boot.jar -Didea.paths.selector=IntelliJIdea13
javaプロセスのGCを監視をする
$ jstat -gcutil <Process ID> 1s

$ jstat -gcutil 8416 3s
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  84.07  59.47  91.95  83.64     17    0.036     2    0.070    0.106
  0.00   0.00  94.05  60.17  92.05  84.34     18    0.044     3    0.146    0.190
  0.00   0.00  19.85  65.32  93.08  86.80     20    0.053     4    0.194    0.246
  0.00  46.71  29.49  65.34  93.09  86.03     21    0.057     4    0.194    0.251
  0.00  46.67   0.00  74.73  93.01  86.12     23    0.062     4    0.194    0.256
 99.55   0.00  57.35  74.93  93.31  86.82     24    0.067     4    0.194    0.261
 99.64   0.00   3.00  78.28  93.66  87.19     26    0.096     5    0.256    0.353
 99.64   0.00  12.49  78.28  93.66  87.19     26    0.096     5    0.256    0.353
 99.64   0.00  33.35  78.28  93.66  87.19     26    0.096     5    0.256    0.353

各カラムの意味については

http://docs.oracle.com/javase/jp/6/technotes/tools/share/jstat.html

を参照。見ればわかるがgcutil以外にも様々なオプションがあるので、参照したい対象ごとに適宜使い分ければいい。

Javaプロセスに負荷をかける

apacheJMeterというツールを用いることで簡単にJavaプロセスに負荷をかけられる。

http://jmeter.apache.org/

GUIツール。使い方は割愛。

設定値の影響効果

・Youngを小さくするとSurvivor0で溢れが生じやすくなり、溢れたオブジェクトはTenuredに移るため、FullGCの発生回数が増える。
・Tenuredを小さくするとFullGCの発生回数が増える
・Tenuredを大きくすると1回のFullGCにかかる時間が増える。
・殿堂入り閾値を小さくすると寿命の短いオブジェクトがTenuredに移り、FullGCの発生回数が増える。
・殿堂入り閾値を大きくすると寿命の長いオブジェクトがTenuredになかなか移らず、Survivor0で溢れが生じやすくなり、FullGCの発生回数が増える。

チューニングの方針

jstatで監視しながら、JMeterで負荷をかけてどのようにGCが行われていくかを見てチューニングを行う。一度にたくさんの値を変えてパフォーマンス測定をしても何が原因で良く(悪く)なったのかわからないため、設定値は1つずつ変えていくと良い。ただし複数の値の合わせ技で良く(悪く)もなりうるので、全ての設定値をアプリケーションに"最適"な値に設定するのは非常に難しい。あくまでGCボトルネックになっているときの対応というスタンスで行うのが現実的だと思う。従って、ボトルネック解消のための明確な目標値を設定してからチューニングを行うことが重要。

基本的には前回の投稿に記述したGCの動作が健全に行われているかどうかを各領域の使用率を参照しながら判断し、「設定値の影響効果」を考慮しつつ設定値を調整していく。
たとえばjstat -gcutilで100%に張り付きっぱなしになっている領域があればそれを広げてやればいい。ただし100%に張り付いている原因がその領域の狭さではない場合は他の設定値を適宜変えていく。
あるいはGCを行った際に解放される領域がだんだん狭くなっていくときはメモリリークが起きているので、ヒープ領域のチューニングではなくアプリケーションのバグ修正を行えばいい。

エスケープ解析

メソッド内で生成され、返り値として使われないオブジェクトはヒープではなくスタックに保持することができる。コンパイラはオブジェクトのスコープを解析(エスケープ解析と呼ばれる)し、ヒープに保持するかスタックに保持するかの判断をする。
この機能はJava SE6u23以降に実装されていて、デフォルトで有効になっている。
スタックにGCはなく高速なので、スコープをメソッド内だけにしてそもそもヒープに入れない実装にすることも、GCによるアプリケーションの停止時間を抑えることにつながる。

Permanent領域

ここまでヒープにはYoung世代とTenured世代があり、という話で進めてきたが、クラス定義等のメタデータを格納しておくためのPermanent世代という領域もヒープ内に存在する。TenuredがいっぱいになったときのFullGC時にはこちらも対象となる。
Permanent領域はYoungやTenuredと違い一定の専有サイズを保っていることが多いので、そのサイズが入る大きさにPermanent領域のサイズを設定しておけばだいたい問題ないと思う。

オプション 説明
-XX:PermSize=? Permanent領域の初期サイズ -XX:PermSize=128m
-XX:MaxPermSize=? Permanent領域の最大サイズ -XX:MaxPermSize=128m

ガベージコレクションの仕組み

Oracleが行っている「JavaSE7 パフォーマンス・チューニング」という研修に行かせてもらえました。
コースの概要はリンク先を参照。

Java SE 7 パフォーマンス・チューニング

知らないことが多くていろいろ学ぶことができたので、このブログに書き留めておきたいと思います。

ガベージコレクションの基本

C++等と違い、JavaではJVMガベージコレクション(以降GC)機能によって参照されなくなった領域をヒープから自動的に開放してくれる。これがどのような仕組みで行われるのかについて記述する。

以下のようなヒープ領域を想定する。(ヒープとはスタックとはみたいな話は省略します)

ヒープ領域
+--------------------------------------------------------------+
|                                                              |
+--------------------------------------------------------------+

オブジェクトを生成するとヒープ上に領域が割り当てられる。
Object obj1 = new Object();

+--------+-----------------------------------------------------+
|  obj1  |                                                     |
+--------+-----------------------------------------------------+

さらに生成するとその分確保される。
Object obj2 = new Object();
Object obj3 = new Object();
Object obj4 = new Object();
Object obj5 = new Object();
Object obj6 = new Object();
Object obj7 = new Object();

+--------+--------+--------+--------+--------+--------+--------+
|  obj1  |  obj2  |  obj3  |  obj4  |  obj5  |  obj6  |  obj7  |
+--------+--------+--------+--------+--------+--------+--------+

ヒープが一杯のになると、新しいオブジェクトのためのヒープ領域を確保するためにGCが発動する。
このときの動作は
1) Mark
参照が残っているかどうかを調べ、残っているものにフラグを立てる。(*がフラグのつもり)(ここでは1,2,5,7の参照だけ残ってると仮定)

+--------+--------+--------+--------+--------+--------+--------+
|* obj1  |* obj2  |  obj3  |  obj4  |* obj5  |  obj6  |* obj7  |
+--------+--------+--------+--------+--------+--------+--------+

2) Sweep
参照の無い領域を開放する。

+--------+--------+--------+--------+--------+--------+--------+
|* obj1  |* obj2  |        |        |* obj5  |        |* obj7  |
+--------+--------+--------+--------+--------+--------+--------+

3) Compaction
ヒープ上でバラバラに残った確保済み領域を寄せる。(大きな領域を確保できるようにするため)

+--------+--------+--------+--------+--------------------------+
|  obj1  |  obj2  |  obj5  |  obj7  |                          |
+--------+--------+--------+--------+--------------------------+

複雑なことを抜きにするとこれがGCの基本的な動作になる。

世代別GC

GCGC用のスレッドで動作し、その間別のスレッドは停止しているため、GCの時間はできる限り短いほうが良い。
上記の簡単なロジックでは無駄が多く時間がかかる。そのため、HotSpot VMでは世代別GCが行われている。世代別GCとは
・多くのオブジェクトは寿命が短い
・一部のオブジェクトは寿命が非常に長い
という2つの経験則を基に効率化されたロジックのGCである。世代別GCでは長く生きたオブジェクトと若いオブジェクトを分けて考える。

+--------Young Generation--------+-----Tenured Generation------+
+----Eden----+Survivor0+Survivor1+
+------------+---------+---------+-----------------------------+
|            |         |         |                             |
+------------+---------+---------+-----------------------------+

ヒープを上記のように分割し、新規にオブジェクトを生成したときはEden領域に領域を確保する
ここで

Young世代

1) Edenが一杯になるとEdenの各オブジェクト参照が残っているかどうかを調べ、残っているものSuvivor0に移動し、Edenを開放する。この時Surivivor0に入りきらなければTenuredに移動する

2) Survivor0とSurvivor1の名前を入れ替える

3) 再びEdenが一杯になると、EdenとSurvivor1の参照が残っているオブジェクトをSurvivor0に移動し、EdenとSurvivor1を開放する。この時Surivivor0に入りきらなければTenuredに移動する

4) Survivor0とSurvivor1の名前を入れ替える

5) 3),4)でJVMに設定した回数(殿堂入り閾値)以上残ったオブジェクトはTenuredに移動する

Tenured世代

1) Tenured領域が一杯になるとヒープ全体に対してMark,Sweep,Compactionを行う

Young世代のGC(マイナーGC)はコピーGCと呼ばれる手法で比較的高速に行われる。Tenured世代のGC(メジャーGC)はFullGCと呼ばれコピーGCの数十倍程度の時間を要する。
このロジックにより、都度FullGCを行うより効率的にGCを行うことができ、プログラムの停止時間を短くすることができる。

各種ヒープの設定

各領域の大きさ等はJVMの起動オプションで指定することにより設定することができる。(指定しない場合はシステム依存)

オプション 説明
-Xms? ヒープの初期サイズ、続けて512kのように書いて指定する。 -Xms128m
-Xmx? ヒープの最大サイズ、続けて128mのように書いて指定する。 -Xms512m
-XX:NewRatio=? young世代とtenured世代のサイズ比率、自然数で指定する。NewRatio=Tenured/Young。 -XX:NewRatio=4
-XX:SurvivorRatio=? Eden領域とSurvivor領域のサイズ比率、自然数で指定する。SurvivorRatio=Eden/2Survivor。 -XX:SurvivorRatio=6
-XX:MaxTenuringThreshold=? 殿堂入り閾値自然数で指定する。 -XX:MaxTenuringThreshold=32

チューニングを行う際はこれらを適切な値を設定してなるべく低コスト(狭いヒープ)で高パフォーマンス(GCによる停止時間が短い)を目指す必要がある。


次の投稿で適切な領域サイズや閾値の設定について書きます。

Tera Termマクロで自動ログイン

sshクライアントTera Termによる自動ログインマクロが便利だったのでメモっておきます。
マクロはTTL(Tera Term Language)で記述する。

Telnet

;
; TELNET LOGIN
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

HOSTADDR = "" ; hostname or ipAddr
USERNAME = ""
PASSFILE = ""

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

PASSKEY = USERNAME
strconcat PASSKEY "@"
strconcat PASSKEY HOSTADDR
getpassword PASSFILE PASSKEY PASSWORD

COMMAND = HOSTADDR
strconcat COMMAND ":23 /nossh /T=1"
connect COMMAND

wait "Login:"
sendln USERNAME
wait "Password:"
sendln PASSWORD

SSH

;
; SSH LOGIN
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

HOSTADDR = "" ; hostname or ipAddr
USERNAME = ""
PASSFILE = ""

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

PASSKEY = USERNAME
strconcat PASSKEY "@"
strconcat PASSKEY HOSTADDR
getpassword PASSFILE PASSKEY PASSWORD

COMMAND = HOSTADDR
strconcat COMMAND ":22 /ssh /user="
strconcat COMMAND USERNAME
strconcat COMMAND " /passwd="
strconcat COMMAND PASSWORD
connect COMMAND

ログイン後の作業の自動化は基本的にシェルスクリプト等で行った方が資料も多いので正しい選択だと思う。

Tera Termマクロを使って便利そうなシチュエーションは
1)自動ログイン
2)サーバ毎に適用するTera Termの設定を変えたい時(ウィンドウサイズやフォントや文字コード等)
3)tmuxscreenの入っていないサーバで複数の端末を使う作業のために、自動で対象サーバへログインした端末を指定個数起動

くらい?思いついたら追記します。

TTLコマンドリファレンス
http://ttssh2.sourceforge.jp/manual/ja/macro/command/index.html

IntelliJがEclipseより優れているわけ

f:id:hiroaki-kono:20140514021425p:plain f:id:hiroaki-kono:20140514020505p:plain
僕は自宅ではIntelliJを使い、職場ではEclipseを使って開発を行っているため、それぞれの違いを感じることが多々ありました。
この投稿ではIntelliJEclipseのそれぞれについて、ある作業を遂行したいときの手間を示し、それぞれについてどちらがより優れているのか考察します。

※貼っている画像について、予約語赤色のものがIntelliJで、緑色のものがEclipseです。

1.補完

結果の質が同じであれば過程はできる限り楽にできる手法が優れている。補完機能無しでJavaの開発を行うととても神経を使い、下らないミスに時間を取られるため、Java開発において補完機能はもはやなくてはならないが、更にその補完機能を使ってなるべく楽をするには、なるべく的確に補完してくれる必要がある。

このようなUtilityクラスのテストしたい場合について考える。

enum RGB {RED, GREEN, BLUE}

class Utility { public static RGB getRed(){ return RGB.RED; } }

IntelliJで下記のようにreとタイプしたところ、第2引数の型(既に与えた情報)を読み取って、第1引数に入るであろう値を候補として挙げてくれた。
f:id:hiroaki-kono:20140513054042p:plain

Eclipsereとタイプしたところ、その候補はreから始まるものを列挙しただけのものだった。
f:id:hiroaki-kono:20140513064946p:plain
IntelliJはコードの文脈を理解し、スマートに補完することができる。
参照:http://www.jetbrains.com/idea/features/code_completion.html
プログラマは文字を正確にタイプすることではなく、プログラムの組み方を考えることに集中していた方が良いので、補完に手間取ることは大きなデメリットになる。

2.デバッグ

デバッグ中に変数に格納された値はどちらのIDEでも簡単に参照できる仕組みになっているが、メソッドの返り値を変数に格納せずに直接評価している場合はその変数のリストには列挙されていない。その返り値がなんであるかを知りたくなった場合について考える。

IntelliJでこれを実現するショートカットはAlt + F8
IntelliJでは評価値を知りたい部分にカーソルを合わせ、Alt + F8と押すとダイアログが出てくる。そこで評価したい式の範囲を選び、Evaluateボタンを押すことで値を知ることができる。
f:id:hiroaki-kono:20140513055859p:plain

Eclipseでこれを実現するショートカットはCtrl + Shift + I
Eclipseで評価値を知りたい部分にカーソルを合わせ、Ctrl + Shift + Iと押しても、カーソルの合っている箇所の最小単位でしか評価してくれないため、以下のようになる。
f:id:hiroaki-kono:20140513223439p:plain
Eclipseでは下記のように、評価したい範囲を正確に選択状態にしてからCtrl + Shift + Iと押すことで値を知ることができる。
f:id:hiroaki-kono:20140513223445p:plain
文字列の正確な範囲選択は神経を使うので、いくつかの候補から選択するのと文字列選択で指定するのとでは、前者の方がプログラマの負担は少なくなる。

3.ソフトラップ

エディタ上で一行が横長になり文字が切れてしまったときに、実際に行は分割せず次の行に表示する機能のことをSoft wrapという。notepad.exeでいう「右端で折り返す」のこと。

IntelliJではあらゆる場所でソフトラップが可能。
f:id:hiroaki-kono:20140515022542p:plain
標準出力ではトグル式のボタンで簡単に切替可能。

f:id:hiroaki-kono:20140515022548p:plain f:id:hiroaki-kono:20140515022549p:plain
一方Eclipseではソフトラップはできない。
f:id:hiroaki-kono:20140515022718p:plain
トレースの結果もラップできない。何がどうAssertionErrorか知るにも一苦労する。
f:id:hiroaki-kono:20140515022719p:plain
コーディング規約を横スクロールが発生しない文字数に定めればエディタ上の問題は大体解決できる。しかしログの出力を見るとき、ものすごい長さの横スクロールが現れて見づらいために、好みのエディタにログをコピペしてから読んだことはないだろうか。この手間から開放されるにはソフトラップ機能を使い、内容を一望できるようにすればよい。

4.パラメータ化

下記のコードについて、RGB.REDメソッドの引数で受けるようにしたい場合について考える。

private void print(){
    System.out.println("test" + RGB.RED);
}

IntelliJでこれを実現するショートカットはCtrl + Alt + P
パラメータ化対象箇所でCtrl + Alt + Pを押すと、まずどの範囲をパラメータ化するか選択できるようになる。
f:id:hiroaki-kono:20140514011652p:plainf:id:hiroaki-kono:20140514011654p:plain
パラメータ化の範囲を選択すると、次はどのようなパラメータ名にするかいくつかの候補を提示してくれる。ここでパラメータ名を選択するか自分で入力すればパラメータ化は完了する。
f:id:hiroaki-kono:20140514012101p:plain

一方Eclipseでこれを実現するショートカットはないらしい。パラメータ化対象箇所を文字列選択してからコンテキストメニューを開き、Introduce parameterを探し出す必要がある。
f:id:hiroaki-kono:20140514013903p:plain
Introduce parameterを探し出して押すとダイアログが開くが、大きめのダイアログのわりに情報は少なく、パラメータ名の候補はnameのみだった。
f:id:hiroaki-kono:20140514014130p:plain
パラメータ化の作業にかかる手間はIntelliJの方が格段に少ない。退屈な作業を強いられるとプログラマはコードに集中できず、作業効率は落ちてしまう。

5.ファイルアイコン・ファイルタブの表示

同じ.javaファイルでもテスト用のクラスだったり、enumクラスだったり、異なる種類のクラスやインターフェースが定義されている。

IntelliJでは同じ.javaファイルでも、内容によってそのアイコンは区別される。
f:id:hiroaki-kono:20140515040010p:plain
Eclipseはされない。(ところでEnumのソースはjdkのsrc.zipにあるEnum.javaを見に行ってるはずなのに、.classファイルとして認識されるらしい)
f:id:hiroaki-kono:20140515040007p:plain
それぞれのアイコンの意味は下記で確認できる。
IntelliJ : http://www.jetbrains.com/idea/webhelp/symbols.html
Eclipse : http://help.eclipse.org/kepler/index.jsp?topic=/org.eclipse.jdt.doc.user/reference/ref-icons.htm

また、IntelliJではファイルタブの多段表示が可能だが、Eclipseでは多段表示に対応していない。そのため開いているファイル数が増えるとタブの表示が省略されてしまう。
IntelliJでもEclipseでもCtrl+Eによって開いているファイルの一覧(IntelliJでは最近開いたファイルも含まれる)を表示して、検索してすぐに開くことができるため、問題にならないかもしれない。しかし開いているファイルを目視できれば何を開いていたか覚えておく必要がなくなるため、その分プログラマはコードに集中することができる。

6.キーマップ

デフォルトのキーマップが気に入らない場合はキーマップを編集する必要がある。そして両方のIDEでキーマップをカスタマイズすることが可能。
そのキーマップのカスタマイズをしたい場合に問題となる、それぞれのUIについて比較した。

IntelliJでは種類毎に分類され、メニューバーの機能やエディタ上のアクション等、階層を辿って目的の項目を見つけることができる。そのほか、機能の名前からの検索、キーマップからの検索が可能。また、デフォルトから変えたキーマップは青文字で表示されている。
f:id:hiroaki-kono:20140515003645p:plain

Eclipseでは項目毎の分類はされていない代わりにカテゴリというカラムが用意されている。検索方法はIntelliJと同様に機能の名前やキーマップに加えて、カテゴリやWhenカラムの値からも検索が可能。マトリクス上の全文字列に対して区別なく検索をかけているようだ。
f:id:hiroaki-kono:20140515003716p:plain
数多くのファイルが、ファイル名に色々情報が書かれて一つのディレクトリに全て置かれた状態と、いくつかのディレクトリに分類された状態なら、ファイル名に書かれた情報の具体的な文字列がわからないときでは後者が強い。一般的な通称のある機能であれば名前はわかるが、詳細な機能の名前まで把握している人は少ない。なるべく無知でも扱えるUIが優れている。

まとめ

IntelliJEclipseで何ができるかはそこまで大きく変わらない、しかしどのようにできるかの部分で大きな差がある。
この投稿では挙げていない多くの些細な部分でも、IntelliJは快適にコーディングさせてくれるのです。