Akira's Tech Notes

Java/JVM | GNU/Linux | Emacs/Lisp | 知的好奇心駆動

header-icon
ネイティブでない日本語で思い付くことや気になることをダラダラ書く、体裁とかは気にしない。読みづらいと感じた時に随時更新する。

[tips][Java]CodeCache領域使用状況の確認方法

1 CodeCacheによる性能劣化事例

先日CodeCacheによる性能問題が発生した、原因を突き止めるには時間が掛かりました。 そのわけは

  1. 全体が遅いではなく、一部処理のみ遅い
  2. スレッドダンプから遅い箇所の特定が出来ない
  3. プロファイリングからホットメソットの特定が出来ない
  4. 性能劣化時間帯に同期化によるロック待ちが発生していない
  5. 性能劣化時間帯のシステムコールのコストは平常時と変わらない
  6. 性能劣化時間帯にメジャーGCが起きていない、マイナーGCの時間も正常数値範囲ないである

よく言われているJVM性能に影響する要素を一通り確認しても、問題の特出が出来なかった。

今回の障害ケースは以下のような特徴があります。

  • アプリケーションサーバ一定時間運転後、特定画面の処理時間が数十倍伸びる
  • アプリケーション再デプロイ後、スローダウン現象が起きやすくなる傾向がある
  • 一度性能劣化すると、アプリケーションサーバを再起動しないと回復しない

この現象はメモリリークによるGC時間が伸びるのような性能劣化ケースと似ている。 しかしGCによるスローダウンは波があるはず、かつ特定画面が遅いとは限らないでしょう。 つまり、CodeCacheによる性能劣化は原因を気づきにくい場合があります。 最終的Flight RecorderのJITコンパイラー記録から問題がCodeCacheにあることを推測した。 OpenJDKご使用の場合 -XX:+PrintCompilation JVM起動フラグでJITコンパイルログから同 じことも確認できる。

スローダウン前に取れた記録

20150901_codecache_jfr_1.png

性能劣化時間帯で取れた記録

20150901_codecahce_jfr_2.png

ご覧の通り、性能劣化時間帯にJITコンパイラーが動いていないようだ。 CodeCache領域の使用量を確認すると、空きがないことも確認されました。 ここで問題はCodeCacheが足りないと推測出来るでしょう。ただ単に推測に過ぎないので、 根拠付けるためにテスト用アプリケーションをデプロイしてJITコンパイラーの挙動を確認しました。 やはりJITコンパイラーが動いていないのだ。

2 CodeCacheについて

CodeCacheについてJava Magazineに BEN EVANS さんの記事が分かりやすいと思います。

以下は自分のまとめです。

  • JITコンパイラーの結果がCodeCacheに格納される
  • CodeCacheが一杯になるとJITコンパイラーが停止してしまう
  • JITコンパイラーが停止すると性能が劣化する(事例: 本来6秒で終わる処理が40秒かかる)
  • アプリを振り返し再デプロイするとCodeCache領域が溢れる可能性がある
  • SpringやJPAのような実行時にClassを動的生成するアプリに置いてCodeCache領域を大量に消費する場合がある
  • JDK7で階層型コンパイラーを有効化するとCodeCache領域の消費が増加する
  • ミドルウェア/アプリケーションサーバのCodeCache領域をチューニングすべき
  • ミドルウェア/アプリケーションサーバのCodeCache領域常時監視しておくべき

[Java道具]jstat -gcの出力を見やすくする

jstat -gc でJVMのメモリ使用状況を確認する際によく使うのですが、少し見づらいと感じま したので、整形用のオレオレスクリプトを作成しました。

jstat出力見づらい要因として

  • 出力単位がbyteとなっています、数字長い時目視でメガバイト(mb)への換算が大変
  • ヘッダ行と値行がズレる時があります

デフォルトの出力形式は以下の通り

$ jstat -gc 10901
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
55296.0 55296.0  0.0    0.0   334336.0 157179.1  890368.0   23266.0   35748.0 32020.9 5248.0 4068.9      3    0.124   2      0.195    0.320

次は自作スクリプトで整形後の出力形式(JDK8の場合)

---------------------------------------------
S0  領域 |   54.00 /    0.00 (MB)  |  0.00%
S1  領域 |   54.00 /    0.00 (MB)  |  0.00%
Eden領域 |  326.50 /  170.63 (MB)  | 52.26%
Old 領域 |   22.72 /  869.50 (MB)  |  2.61%
Meta領域 |   31.27 /   34.91 (MB)  | 89.57%
CCPS領域 |    3.97 /   54.00 (MB)  | 77.53%
---------------------------------------------
YGC 回数 |       3 回
YGC 時間 |    0.12 秒
FGC 回数 |       2 回
FGC 時間 |    0.20 秒
---------------------------------------------

コードはgithubに公開しています。

jvm_memstst.sh

出力加工機能以外、次の便利な機能も提供しています。

  • JavaプロセスID自動特定する
  • JAVA_HOMEを自動的に特定する

[tips][Linux]旧バージョンCentOSでyum更新できなくなった時

1 CentOS 6.6でyum更新したら404エラー

CentOS 6.6でyum更新したら404エラーとなって更新できなくなった。

# yum update
読み込んだプラグイン:fastestmirror
更新処理の設定をしています
Loading mirror speeds from cached hostfile
 * base: ftp.iij.ad.jp
 * extras: ftp.iij.ad.jp
 * updates: centos.usonyx.net
http://ftp.iij.ad.jp/pub/linux/centos/6.6/os/x86_64/repodata/repomd.xml: [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 404 Not Found"
他のミラーを試します。
http://ftp.jaist.ac.jp/pub/Linux/CentOS/6.6/os/x86_64/repodata/repomd.xml: [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 404 Not Found"
他のミラーを試します。
http://ftp.nara.wide.ad.jp/pub/Linux/centos/6.6/os/x86_64/repodata/repomd.xml: [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 404 Not Found"
他のミラーを試します。
http://ftp.riken.jp/Linux/centos/6.6/os/x86_64/repodata/repomd.xml: [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 404 Not Found"
他のミラーを試します。

ブラウザからミラーサイトを直接確認するとreadmeファイルしかありませんでした。

20150829231548_CentOS_yum_404.png

readmeの内容は下記となります。

$ curl http://ftp.iij.ad.jp/pub/linux/centos/6.6/readme
This directory (and version of CentOS) is deprecated.  For normal users,
you should use /6/ and not /6.6/ in your path. Please see this FAQ
concerning the CentOS release scheme:

https://wiki.centos.org/FAQ/General

If you know what you are doing, and absolutely want to remain at the 6.6
level, go to http://vault.centos.org/ for packages.

Please keep in mind that 6.0, 6.1, 6.2, 6.3, 6.4 , 6.5 and 6.6 no longer gets any updates, nor
any security fix's.

簡単に言うと更新が止まった旧バージョンのパッケージは http://vault.centos.org/ ドメイン下に移管された。

というわけでyumリポジトリのURLを書き換えれば問題が解消される。

$ sudo sed -i -e "s|mirror\.centos\.org/centos/\$releasever|vault\.centos\.org/6.6|g" /etc/yum.repos.d/CentOS-Base.repo
$ sudo sed -i -e "s|#baseurl=|baseurl=|g" CentOS-Base.repo
$ sudo sed -i -e "s|mirrorlist=|#mirrorlist=|g" CentOS-Base.repo
#  yum update
読み込んだプラグイン:fastestmirror
更新処理の設定をしています
Loading mirror speeds from cached hostfile
base                                                                                                                                   | 3.7 kB     00:00     
extras                                                                                                                                 | 3.4 kB     00:00     
updates                                                                                                                                | 3.4 kB     00:00     
(中略)

[tips][Java]OpenJDK8付属ツール実行時エラーの対策

本記事のOpenJDK障害は次の環境で確認しています。

$ java -version
openjdk version "1.8.0_60"
OpenJDK Runtime Environment (build 1.8.0_60-b24)
OpenJDK 64-Bit Server VM (build 25.60-b23, mixed mode)

$ uname -a
Linux mimi 4.1.6-1-ARCH #1 SMP PREEMPT Mon Aug 17 08:52:28 CEST 2015 x86_64 GNU/Linux

[tips][Linux]Firefox起動時にプロファイル選択出来るようにする

Web開発でクリーンなブラウザテスト環境が必要とする場面が結構あります。 普段利用しているブラウザの設定をリセットやキャッシュクリアは平気に出来ないでしょう。

この問題はブラウザのマルチプロファイル機能を利用すればを解決する。

例えばFirefoxに --new-instance オプションを付けて起動時すると下記のようなプロファイ ル選択ダイアログが表示されて、「Create Profile」で新しいプロファイルを持ちたFirefoxが 起動される。

2015-08-07-firefox-new-instance.png

自分の環境では下記のdesktopファイルを追加することでランチャーからいつも起動出来るよう にしています。

.local/share/applications/myfirefox.desktop

[Desktop Entry]
Name=MyFirefox
GenericName=Web Browser
GenericName[ja]=ウェブ・ブラウザ
Comment[ja]=ウェブを閲覧します
Exec=firefox --new-instance %u
Terminal=false
Type=Application
Icon=firefox
Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml+xml;application/vnd.mozilla.xul+xml;text/mml;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;
StartupNotify=true
Actions=NewTab;NewWindow;NewPrivateWindow;

[Desktop Action NewTab]
Name[ja]=新しいタブ

[Desktop Action NewWindow]
Name[ja]=新しいウィンドウ

[Desktop Action NewPrivateWindow]
Name[ja]=新しいプライベートウィンドウ

[Memo][Linux]bash環境にpecoを導入しました

以前から percol の流行りに気になっていたが、python製のため導入が面倒さそうなので保留 しました。最近、percolを元にgolangで書かれたpecoが出てきて導入しやすくなったと感じて 手を入れました。

[tips][Linux]Sambaユーザのパスワード変更

普段はLinuxで作業しているけど、MS系のドキュメントメンテ作業はWindowsでやらざるを得な いのでSambaでLinux側のコンテンツをWindowsに共有するようにしていました。

ところで先週Windows端末再セットアップしたため、Sambaサーバに接続時にパスワードが聞か れた時が、すっかり忘れましたのでSamba側でパスワードリセットをしました。

  • パスワードリセットコマンド(ユーザ登録コマンドと一緒)
    $ sudo pdbedit -a -u test001
    new password:
    retype new password:
    
  • ついにパスワード情報の格納先を確認しました
    $ pdbedit -L
    tdbsam_open: Failed to open/create TDB passwd [/var/lib/samba/private/passdb.tdb]
    tdbsam_getsampwnam: failed to open /var/lib/samba/private/passdb.tdb!
    User Search failed!
    

[tips][Linux]Dockerのプロキシ設定

基本は本家のドキュメントを見ろう!

https://docs.docker.com/articles/systemd/

ここでは Acrh Linx で確認された手順を記録する。

社内はフォワードプロキシ経由で外と繋ぐなので、デフォルトでは通らない ;-(

$ docker search oraclelinux
Error response from daemon: Get https://index.docker.io/v1/search?q=oraclelinux: dial tcp 54.174.226.171:443: no route to host

/etc/systemd/system/<サービスID>.d/ 配下に環境変数の上書きファイルを定義し、サービ スをリロードするだけです。

$ sudo mkdir /etc/systemd/system/docker.service.d
$ sudo touch /etc/systemd/system/docker.service.d/http-proxy.conf
$ sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://*********************:8081"
Environment="HTTPS_PROXY=http://*********************:8081"
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
$ docker search oraclelinux
NAME                                      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
oraclelinux                               Oracle Linux is an open-source operating s...   46        [OK]       
oracle/oraclelinux                        Oracle Linux is an open-source operating s...   16                   [OK]
tvierling/oraclelinux                     Oracle Linux base images, yum-updated to l...   2                    [OK]
centminmod/oraclelinux65base              Oracle Linux 6.5 base 1.57GB image              1                    
avmiller/oraclelinux                      Personal Oracle Linux Test Images (Non-pro...   0                    
kiwenlau/oraclelinux                                                                      0                    
talberto/oraclelinux                                                                      0                    
ksasi/oraclelinux                                                                         0                    
saltfactory/oraclelinux                   Oracle Linux                                    0                    
arpagaus/oraclelinux                                                                      0                    
joseperez/oraclelinux-lamp                oraclelinux 7.0 con mariadb, apache y php.      0                    
jinyan/oraclelinux                        Oracle Linux 6.6                                0                    
hootjr/oraclelinux                                                                        0                    
poisoncreed/oraclelinux-base-with-httpd                                                   0                    
bdpzone/bdporaclelinux6                   Big Data Partnership Development Environme...   0                    [OK]
dyoung522/oracle-ruby                     OracleLinux running Ruby                        0                    [OK]
bdpzone/bdporaclelinux7                   Big Data Partnership Development Environme...   0                    [OK]
mlechner/oraclelinux7                     GIS tools on Oracle Linux 7                     0                    
tehmul/oraclelinux6-tc                    Oracle Linux 6+Oracle Server JRE 8+ Apache...   0                    
calaniz/oraclelinux                                                                       0                    
dyoung522/oraclelinux-dev                 OracleLinux with development environment        0                    [OK]
hedlund/oraclelinux                                                                       0                    [OK]
florentbenoit/oraclelinux-jdk7                                                            0                    [OK]

[Emacs]mew起動時にアカウントの選択を出来るように

久々のemacsネタです。

mewで複数メールアカウントで扱うときにSummary モードにおいて C で切り替えることが出 来るので非常に便利です。ただし、起動時にdefaultアカウントの選択機能が持っていないので、 mew-init-hookで拡張してみました。

;; アカウント1
(setq my-mew-config-1
      (list
       '(proto                          "+")
       '(name                           "******")
       '(user                           "******")   ; メールアドレスの@から左部分
       '(mail-domain                    "******")   ; メールアドレスの@から右部分
       '(smtp-server                    "******")   ; 送信用サーバアドレス
       '(smtp-port                      "1025")     ;
       '(smtp-user                      "******")   ; 送信用ユーザid
       '(pop-server                     "******")   ; 受信用サーバアドレス
       '(pop-port                       "1110")     ;
       '(pop-user                       "******")   ; 受信用ユーザid
       '(pop-delete                     t)          ; メール受信後サーバー側のメッセージを7日を保持する
       '(pop-auth                       pass)       ; pop3 で受信時の認証方式、指定しない場合はapop方式が使用される
       '(pop-size                       0)          ; メールの上限サイズ。0 の場合は上限なし。
       '(pop-header-only                t)          ; [c-u s]でヘッダの みのコピー
       '(dcc                            "******")   ; 返信時に自分のアドレスをdccに入れる、dccは送信したメールのヘッダーに表示されない
       '(fcc                            "+sent")    ; 送信したメールの保存先
       '(smime-signer                   "******")   ; 電子署名で使用する証明書id
       '(protect-privacy-always         nil)
       '(privacy-method                 smime)      ; 電子署名タイプの指定
       '(draft-privacy-method           smime)
       '(protect-privacy-always-type  smime-signature)
       ))

;; アカウント2
(setq my-mew-config-2
      (list
       '(proto                   "%")
       '(name                    "******")
       '(user                    "******")
       '(mail-domain             "gmail.com")
       '(dcc                     "*******@gmail.com")
       '(protect-privacy-always  nil)
       '(ssl-verify-level        0)
       ;; smtpサーバー
       '(smtp-user               "*******@gmail.com")
       '(smtp-auth               t)
       '(smtp-ssl                t)
       '(smtp-server             "smtp.gmail.com")
       '(smtp-ssl-port           "465")
       ;; imapを使用する場合
       '(inbox-folder            "%gmail")
       '(imap-user               "******@gmail.com")
       '(imap-size               5242880) ; 5m以内即時受信する
       '(imap-auth               t)
       '(imap-ssl                t)
       '(imap-ssl-port           "993")
       '(imap-server             "imap.gmail.com")
       ))

;;; アカウント3
(setq my-mew-config-3
      (list
       '(proto                   "+")
       ;; (中略)
))

;;; アカウント4
(setq my-mew-config-4
      (list
       '(proto                   "+")
       ;; (中略)
))

(setq my-mew-account-list
      (list (append (list 'account1) my-mew-config-1)
            (append (list 'account2) my-mew-config-2)
            (append (list 'account3) my-mew-config-3)
            (append (list 'account4) my-mew-config-4)))

(require 'dash)

;; アカウント選択処理
(defun my-mew-select-account ()
  (let* ((account-name-list (mapcar (lambda (x) (pp-to-string (car x))) my-mew-account-list))
         (selected-account (completing-read "mew account: " account-name-list nil t)))
    (message selected-account)
    (setq mew-config-alist
          (append
           (list
            (append (list 'default)
                    (cdr
                     (car (-filter (lambda (mew-config)
                                     (string= selected-account
                                              (pp-to-string (car mew-config))))
                                   my-mew-account-list)))))
           (-filter (lambda (mew-config)
                      (not
                       (string= selected-account
                                (pp-to-string (car mew-config)))))
                    my-mew-account-list)))))

;; 起動時に走らせる
(add-hook 'mew-init-hook 'my-mew-select-account)

デモ