タイトル/名前 | 更新者 | 更新日 |
---|---|---|
Java/SPI(Service Provider Interface) | msakamoto-sf | 2014-12-23 13:04:06 |
Java/文字コードメモ5, Stringコンストラクタの内部実装, ISO-8859-1とUS-ASCIIの違い, CoderResultのパフォーマンス問題 | msakamoto-sf | 2014-11-30 21:46:58 |
日記/2014/11/30/OWASP Day(2014-11-29)関連リンク | msakamoto-sf | 2014-11-30 18:22:19 |
Java/DB操作調査メモ(JPA 2.0, JDBI, Connection Pool, 2-way SQL) | msakamoto-sf | 2014-11-16 22:08:48 |
技術/運用管理/Vagrant/Vagrantfile書き方メモ | msakamoto-sf | 2014-11-16 20:58:04 |
Java/Profile/EclipseでのProfilerツール, 2014-09時点 | msakamoto-sf | 2014-09-22 22:33:23 |
技術/Linux/CentOS/CentOS6系でのyum-cronによるパッケージ自動更新 | msakamoto-sf | 2014-09-22 20:41:44 |
技術/Linux/CentOS/CentOS6でのcron, anacron | msakamoto-sf | 2014-09-22 19:58:51 |
技術/Linux/CentOS/日本語サポート, 日本語入力のインストール | msakamoto-sf | 2014-09-16 00:00:02 |
Java/文字コードメモ4 | msakamoto-sf | 2014-09-15 20:56:07 |
Javaではライブラリはjarで配布される場合が多い。その中でもインターフェイスと実装をjar単位で分離して、サードパーティによる実装の拡張が容易になるような仕組みが"SPI"(Service Provider Interface)と呼ばれており、JDK5までは "sun.misc.Service" という非公開クラスとメソッドだったのが、JDK6以降は "java.util.ServiceLoader" クラスとして公開され、利用できるようになった。
SPIの良い例がJDBCの処理で、インターフェイスは定義しておき、それを実装するライブラリを、DBMSごとに用意する。プログラマはSPI経由でJDBCインターフェイスを取得すれば、実際のどのDBMSの実装クラスが使われたのかを意識せずに、すべて同じインターフェイス(クラス名・メソッド名)でDB接続を操作できる。なおアプリを配布する際は、使用するDBMSのJDBC用jarファイルも一緒に配布する。
簡単なユースケースを以下のAntプロジェクトで試してみた:
仕組みとしては、実装側のjarファイルの "META-INF/services/" の下に、実装したいインターフェイスのクラス名でファイルを作成し、その中に実装ファイルのクラスを一行一個で記載する。
今回のサンプルでは、FooProviderが "spidemo.cloud.spi.Cloud" と "spidemo.search.spi.Search" の2つのインターフェイスの実装を提供するため、"META-INF/services/"に以下の2ファイルを配置している。
FooProvider/src/META-INF/services/spidemo.cloud.spi.Cloud:
spidemo.cloud.FooCloud
FooProvider/src/META-INF/services/spidemo.search.spi.Search:
spidemo.search.FooSearch
また、BarProviderでは "spidemo.search.spi.Search" に対して2つの実装クラスを提供するため、 以下のように2行にして2つのクラス名を記述している。
BarProvider/src/META-INF/services/spidemo.search.spi.Search:
spidemo.search.BarSearch spidemo.search.BarSearch2
mainクラスはDemoAppに配置しているが、DemoAppそれ自身でもCloudとSearchの両インターフェイスを実装して、自分自身の実装をロードできるか確認する。
DemoApp/src/META-INF/services/spidemo.cloud.spi.Cloud:
spidemo.cloud.MyCloud
DemoApp/src/META-INF/services/spidemo.search.spi.Search:
spidemo.search.MySearch
実行時の出力(解説付き):
# CloudService インターフェイスの実装としてFooProvider, BazProviderの2つを用意した。 # また、DemoApp自身が "My Cloud Provider" という名前の実装を含んでいる。 # getProviderName()とgetServiceNames()をprintしている。 Provider Name: My Cloud Provider My Container My Tomcat Provider Name: Foo Cloud Provider Foo VPC Foo VPN Foo Shared Server Foo Dedicated Server Provider Name: Baz Cloud Provider Baz Xen Computing Baz Security Gateway Baz Shared Storage # SearchService インターフェイスの実装としてFooProvider, BarProviderの2つを用意した。 # (なお、FooProviderはCloudとSearchの2つとも実装している) # また、DemoApp自身が "My Cloud Provider" という名前の実装を含んでいる。 Provider Name: My Search Provider My Search 1 My Search 2Provider Name: Foo Search Provider Foo Search 1 Foo Search 2 Foo Search 3 ## BarProviderでは、Search SPIに対して2つの実装を定義している。 Provider Name: Bar Search Provider Bar Search 1 Bar Search 2 Bar Search 3 Provider Name: Bar Search2 Provider Bar2 Search 1 Bar2 Search 2 Bar2 Search 3
参考:
日本語資料:
Java/文字コードメモ4 に続いたメモ5.
Oracle JDK 7(jdk7u25)のJDKソースを確認してみる。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[];
→Stringクラスのコンストラクタは、いずれも最終的にvalueというchar配列に値を設定してる。
参考:
"String(byte[], int, int, Charset)"のコンストラクタから追ってみる。
JDK7u25のソースを参考にしている。
public String(byte bytes[], int offset, int length, Charset charset) { // (...) this.value = StringCoding.decode(charset, bytes, offset, length); }
StringCodingはpackage privateスコープのクラスのため、未公開。JDKのソースで確認する。細かいエラー処理とかは思い切って省略すると以下の流れになっている。
static char[] decode(Charset cs, byte[] ba, int off, int len) { // Charset#newDecoder()から、CharsetDecoderインスタンスを取得 CharsetDecoder cd = cs.newDecoder(); // 1バイト辺りの最大文字数とlenより、最大の文字数を計算 int en = scale(len, cd.maxCharsPerByte()); // 最大の文字数でchar配列を作成 char[] ca = new char[en]; // CharsetDecoderの準備 cd.onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .reset(); if (cd instanceof ArrayDecoder) { // CharsetDecoderがsun.nio.cs.ArrayDecoderの派生クラスならそのままdecode()メソッドを呼ぶ。 int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca); return safeTrim(ca, clen, cs, isTrusted); } else { // sun.nio.cs.ArrayDecoderの派生クラスでなければ、 // ByteBufferとCharBufferにラップしてdecode()メソッドを呼ぶ。 ByteBuffer bb = ByteBuffer.wrap(ba, off, len); CharBuffer cb = CharBuffer.wrap(ca); try { CoderResult cr = cd.decode(bb, cb, true); if (!cr.isUnderflow()) cr.throwException(); cr = cd.flush(cb); if (!cr.isUnderflow()) cr.throwException(); } catch (CharacterCodingException x) { // Substitution is always enabled, // so this shouldn't happen throw new Error(x); } return safeTrim(ca, cb.position(), cs, isTrusted); } }
CharsetDecoderの実装については後述するが、JDKで提供されているものについては "sun.nio.cs" パッケージ以下で実装されている。
インターフェイスとしては java.nio.charset.CharsetDecoder abstract class として公開されている。
それまでのJavaでは、String.substring()の結果は、元のStringインスタンスの"char[] value"をそのまま参照し、beginとendだけを変更したインスタンスを返していた。
その場合、巨大なテキストデータから文字列を切り出す処理を考えると、元のテキストデータが不要になっても、切りだされたStringインスタンスが巨大な"char[] value"を参照しつづけ、GCで回収されない。
これによるメモリリークが問題視されたらしく、Java 7u6 では、元の"char[] value"から必要な範囲だけをコピーして新規に作成する形式に変更された。これにより、不要になった元の巨大なテキストデータが適切にGCで回収されるようになる。
参考:
実際にJDKのソースを確認してみる。
JDK6のString.substring()の流れを確認する:
http://grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/6-b27/java/lang/String.java/?v=source
public String substring(int beginIndex, int endIndex) { // (...) return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value); }
→ String(int, int, char[]) のコンストラクタで生成したのを返している。このコンストラクタは以下のようにpackage privateとなっており、外部には公開されていない。
String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; }
このコンストラクタでは、確かに、元の"char[] value"をそのまま引き継ぎ、offsetとcountだけを更新している。このため、元のStringが巨大なテキストデータだった場合、その"char[] value"で本来は使われない領域が参照され続ける。
これにより、元の巨大なテキストデータが不要になっても、substring()されたインスタンスの方で参照が残っているため、不要なデータがGCに回収されない。(substring()された新しいStringインスタンスが不要になってようやくGC対象になる。)
一方、JDK 7u40 のソースを確認してみる。
http://grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/String.java/?v=source
public String substring(int beginIndex, int endIndex) { // (...) return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); }
→JDK6とは異なり、String(char[], int, int)のコンストラクタを呼んでいる。
public String(char value[], int offset, int count) { // (...) this.value = Arrays.copyOfRange(value, offset, offset+count); }
新しく配列をコピーして作ったのを、"this.value"に設定している。
JDK7の場合:
Oracle JDK 7(jdk7u25)のJDKソースを確認してみる。
java.lang.Stringのコンストラクタ:
public String(byte bytes[], int offset, int length) { checkBounds(bytes, offset, length); this.value = StringCoding.decode(bytes, offset, length); }
→ StringCodingはpackageスコープのクラスのため、未公開。JDKのソースを確認する。
java.lang.StringCoding:
class StringCoding { // (...) static char[] decode(byte[] ba, int off, int len) { String csn = Charset.defaultCharset().name(); try { // use charset name decode() variant which provides caching. return decode(csn, ba, off, len); } catch (UnsupportedEncodingException x) { warnUnsupportedCharset(csn); } try { return decode("ISO-8859-1", ba, off, len); } catch (UnsupportedEncodingException x) { // If this code is hit during VM initialization, MessageUtils is // the only way we will be able to get any kind of error message. MessageUtils.err("ISO-8859-1 charset not available: " + x.toString()); // If we can not find ISO-8859-1 (a required encoding) then things // are seriously wrong with the installation. System.exit(1); return null; } }
→最初にCharset.defaultCharset()でCharset名を取得し、StringCoding.decode()でデコードを試みてる。それに失敗したら、"ISO-8859-1"でデコードできないか試してみて、ソレでも駄目ならISO-8859-1でエラーになるというのはシステムレベルでおかしいので、JVMを終了(System.exit(1))してる。
Charset.defaultCharset()はどこからCharset名を取り出してるのか?これも、Charset.defaultCharset()の実装をJDKのソースで確認する。
public static Charset defaultCharset() { if (defaultCharset == null) { synchronized (Charset.class) { String csn = AccessController.doPrivileged( new GetPropertyAction("file.encoding")); Charset cs = lookup(csn); if (cs != null) defaultCharset = cs; else defaultCharset = forName("UTF-8"); } } return defaultCharset; }
→ "file.encoding" システムプロパティから取得している。"file.encoding"の値がおかしかったら、"UTF-8"を試してる。
US-ASCIIは7bitの範囲の文字までしかマッピングしていないことから、US-ASCIIはISO-8859-1のサブセットと考えることができる。
OpenJDK7だと、sun.nio.cs パッケージ以下にあるぽい。
Java/文字コードメモ4 で軽く触れた、"java.nio.charset.CoderResult" について再考する。
java.nio.charset.CoderReusltクラスは、java.nio.charset.CharsetDecoder#decode() や java.nio.charset.CharsetEncoder#encode() などの処理結果を表現するクラスになっている。
byte配列をdecodeする、あるいはUnicode文字の配列をbyte配列にencodeする処理はどうしても状態遷移が伴う処理であり、入力バッファ・出力バッファの長さや中途半端なデータ、対応していないデータなど様々な異常処理が発生し得る。
そのため、CharsetDecoder/CharsetEncoderの実装は、内部でCoderResultに適切な異常状態の詳細を設定して、呼び出し元に返す仕組みになっている。
http://docs.oracle.com/javase/jp/7/api/java/nio/charset/CoderResult.html
「長さが中途半端」なパターンが頻用されるのか、専用のstatic factoryが用意されている。
このstatic factoryが、内部で length に対応するCoderResultのインスタンスをstaticにキャッシュしている。これが CoderResultクラスに対するsynchronizedブロックでスレッドセーフティを確保している。
つまりこれが、マルチスレッド(正確にはClassLoaderに対してとなるが、システムクラスなので事実上システム全体)間でのロックにつながるsynchronizedブロックになる。
以下が上記2つのstatic factoryのJDK7u25におけるソース。
public static CoderResult malformedForLength(int length) { return malformedCache.get(length); } public static CoderResult unmappableForLength(int length) { return unmappableCache.get(length); }
"malformedCache", "unmappableCache"ともにCoderResultのstaticメンバ。
private static Cache malformedCache = new Cache() { public CoderResult create(int len) { return new CoderResult(CR_MALFORMED, len); }}; private static Cache unmappableCache = new Cache() { public CoderResult create(int len) { return new CoderResult(CR_UNMAPPABLE, len); }};
"Cache"はCoderResultクラス内のインナークラスとして宣言されており、"create(int len)"をabstractとしている。
private static abstract class Cache { private Map<Integer,WeakReference<CoderResult>> cache = null; protected abstract CoderResult create(int len); private synchronized CoderResult get(int len) { if (len <= 0) throw new IllegalArgumentException("Non-positive length"); Integer k = new Integer(len); WeakReference<CoderResult> w; CoderResult e = null; if (cache == null) { cache = new HashMap<Integer,WeakReference<CoderResult>>(); } else if ((w = cache.get(k)) != null) { e = w.get(); } if (e == null) { e = create(len); cache.put(k, new WeakReference<CoderResult>(e)); } return e; } }
これにより、CR_MALFORMED/CR_UNMAPPABLEにそれぞれ対応する、長さ毎のCoderResultインスタンスをstaticかつスレッドセーフにキャッシュしている。
確かにクラスのstaticメンバに対するsynchronizedなのでマルチスレッド間でグローバルにロックされてしまう。しかし、これがチューニングレベルで影響してしまうのはどのような時か?
まず、これらのstatic factoryは長さが不正であったり文字とのマッピングに失敗している状態を表している。つまり、異常処理で呼ばれるのを意図している。
したがって、大抵の時間が正常処理に費やされる一般的なシステムであれば、これらのstatic factoryが呼ばれることはほとんど無いと考えられ、チューニングレベルで影響することも無いと考えられる。
チューニングに影響するレベルとしては、システムが想定している文字コードとは異なるデータを大量に扱う状態が考えられる。
ただその場合、当然decode/encodeが正常に終了していないため、結果として文字化けなどが大量発生してすぐに異常に気づくだろう。
あるいはそもそもどんな文字コードがくるか分からず、アスキー範囲だけを文字として扱いたい場合に、UTF-8などdecode/encodeで状態遷移が必要なCharsetを使ってしまっているケースが考えられる。
バイナリデータを無理やり、アスキー範囲だけでも文字として扱う際に、UTF-8を使ってStringにすると、UTF-8に従わないbyte[]部分でunmappableが発生する可能性がある。
つまり定常的に文字コード処理の異常が発生している状態であり、そのようなユースケースだと体感レベルでパフォーマンスに影響してしまうかもしれない。
解決策としては、システムの特性によっていくつか考えられる。
2014-11-29 開催のOWASP Dayで紹介されていたURLやスライド資料のリンクのメモ。
参考資料リンク集
JPA入門
JPQLのパフォーマンス
アノテーションにSQLを記述してコード量を減らせる、独特なライブラリ。
Apache Commons DBCP : バージョン2系が出てる。
Tomcat JDBC Pool
参考:
ポイント:
Vagrantfileの例1:
# -*- mode: ruby -*- # vi: set ft=ruby : # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # All Vagrant configuration is done here. The most common configuration # options are documented and commented below. For a complete reference, # please see the online documentation at vagrantup.com. # 共通して使うBOX名を書いておく。 config.vm.box = "hashicorp/precise64" config.vm.define "web" do |node| node.vm.hostname = "web" # お互いに通信できるよう、VirtualBox でのHostOnly networkを使う。 node.vm.network :private_network, ip: "192.168.56.51" end config.vm.define "db" do |node| node.vm.hostname = "db" # お互いに通信できるよう、VirtualBox でのHostOnly networkを使う。 node.vm.network :private_network, ip: "192.168.56.52" end end
→ "vagrant ssh web", "vagrant ssh db" でSSH接続できる。
Vagrantfileの例2:
VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.define "web" do |node| # それぞれでBOX名を指定 node.vm.box = "hashicorp/precise64" node.vm.hostname = "web" node.vm.network :private_network, ip: "192.168.56.51" end config.vm.define "db" do |node| # それぞれでBOX名を指定 node.vm.box = "hashicorp/precise64" node.vm.hostname = "db" node.vm.network :private_network, ip: "192.168.56.52" end end
Eclipse上でのJavaプロファイリングツールについてですが、オープンソースや有償製品がいくつかあります。
OSS, 無償製品
有償製品
参考:
EclipseのHelios(3.6)までは Eclipse Test & Performance Tools Platform Project がEclipse本体が開発しているプロファイリングツールとして有力でした。
しかし、Eclipse TPTPは2010~2011年頃に開発が停止したため、Indigo(3.7)以降はTPTPは使えません。
CentOS6系では、yum-cronを使うとパッケージ更新の自動チェック/DL/アップデートを簡単に行える。
インストール:(依存パッケージは yum-plugin-downloadonly)
# yum install yum-cron
構成:
# rpm -ql yum-cron /etc/cron.daily/0yum.cron /etc/rc.d/init.d/yum-cron /etc/sysconfig/yum-cron /etc/yum/yum-daily.yum /etc/yum/yum-weekly.yum /usr/share/doc/yum-cron-3.2.29 /usr/share/doc/yum-cron-3.2.29/COPYING /usr/share/man/man8/yum-cron.8.gz
※ 技術/Linux/CentOS/CentOS6でのcron, anacron でも紹介しているが、CentOS6系の最新だと、"/etc/cron.daily" 以下は cron ではなく anacron により実行される。
起動:yum-cronはcronジョブであるが、serviceとして起動しておく必要がある。serviceとして起動するとロックファイルが作成され、yum-cronのジョブはそれを見て実際の処理を開始する。
# service yum-cron start Enabling nightly yum update: [ OK ] # chkconfig yum-cron on # chkconfig --list | grep yum-cron yum-cron 0:off 1:off 2:on 3:on 4:on 5:on 6:off
設定:
/etc/sysconfig/yum-cron の、以下の3項目がメイン。
# Don't install, just check (valid: yes|no) CHECK_ONLY=no # Check to see if you can reach the repos before updating (valid: yes|no) CHECK_FIRST=no # Don't install, just check and download (valid: yes|no) # Implies CHECK_ONLY=yes (gotta check first to see what to download) DOWNLOAD_ONLY=no
上記はデフォルト値で、CHECK_ONLY も DOWNLOAD_ONLY も no なので、自動的にチェック&インストールまで実施される。
DOWNLOAD_ONLY=yesにしておくと、以下のようなメールが届く。
題名:Anacron job 'cron.daily' on (ホスト名) 本文: /etc/cron.daily/0yum.cron: New updates available for host (ホスト名) audit.x86_64 2.2-4.el6_5 updates ... Updates downloaded, use "yum -C update" manually to install them.
"yum -C update" を行うと、プロンプト付きでの更新になる。
# yum -C update Loaded plugins: downloadonly, fastestmirror, presto Setting up Update Process Resolving Dependencies --> Running transaction check (...) shared-mime-info x86_64 0.70-4.el6 base 209 k Transaction Summary ============================================================================================================================================ Install 7 Package(s) Upgrade 89 Package(s) Total size: 103 M Is this ok [y/N]: N
"yum -C -y update" で自動的に行員をインストールする。
# yum -C -y update (...) Installed: kernel.x86_64 0:2.6.32-431.29.2.el6 (...) Complete!
更新前:
# uname -a Linux ip-10-120-73-205 2.6.32-358.6.2.el6.x86_64 #1 SMP Thu May 16 20:59:36 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
更新→再起動後:更新に成功している。
# uname -a Linux ip-10-120-73-205 2.6.32-431.29.2.el6.x86_64 #1 SMP Tue Sep 9 21:36:05 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
除外パッケージや対象パッケージの絞り込みは出来るか?
参考リンク:
以前、ローカルでCentOS 6.3をDVDイメージからセットアプしたときは気づかなかったが、最近AWSのEC2で、Marketplace提供のCentOS 6.4 or 6.5のAMIを使ってみたら気づいた。
実行形態
cronとanacronの違いの例として、毎日 03:00 に実行するジョブをcronとanacronとでそれぞれ登録してみる。anacronは毎時5分に "/etc/cron.hourly" ディレクトリ以下のスクリプトにより、cronから実行される。
/etc/crontab
0 3 * * * root /foo/bar/job.sh
/etc/anacrontab
... # anacronの場合は、ぴったり何時に実行ではなく、実行する時間帯を指定。3時-5時に設定。 START_HOURS_RANGE=3-5 #period in days delay in minutes job-identifier command 1 5 foo.job /foo/bar/job2.sh
メンテナンスのため00:00 - 04:00 までそのマシンが停止するケースだと、以下のような動きになる。
00:00 - マシンシャットダウン 03:00 - シャットダウン中なので、cronもanacronも実行されない。 04:00 - マシン起動 04:05 - cron.hourlyによりanacronが実行される。 → START_HOURS_RANGE内なので、"foo.job"の前回起動した日時を /var/spool/anacron/(job-identifier) ファイルから読み取り、 "period in days" 経過していれば実行する。
このように、cronではジョブ実行時間帯にマシンが停止していればそのジョブは実行されない。
anacronであれば、日数間隔で大雑把にはなるが、マシンが起動したあとの最初のanacron実行時に、停止している間に実行されるべきだったジョブを実行できる。
cron が、vixie-cron から Fedoraプロジェクトでforkされた cronie というパッケージに変更されている。
CentOS 6.4 AWS Marketplace AMI (yum update後):
# rpm -qa | grep cron cronie-1.4.4-12.el6.x86_64 crontabs-1.10-33.el6.noarch cronie-anacron-1.4.4-12.el6.x86_64
crondデーモン本体を cronie が、スケルトンの crontab ファイル・ディレクトリを crontabsパッケージが提供している。
# rpm -ql cronie /etc/cron.d/0hourly /etc/cron.deny /etc/pam.d/crond /etc/rc.d/init.d/crond /etc/sysconfig/crond /usr/bin/crontab /usr/sbin/crond ... /var/spool/cron
/etc/cron.d/0hourly:
SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/ 01 * * * * root run-parts /etc/cron.hourly
crontabsパッケージの内容:
# rpm -ql crontabs /etc/cron.daily /etc/cron.hourly /etc/cron.monthly /etc/cron.weekly /etc/crontab /usr/bin/run-parts /usr/share/man/man4/crontabs.4.gz
/etc/crontab:
# more /etc/crontab SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/ # For details see man 4 crontabs # Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * user-name command to be executed
anacronの実行ファイルと、デフォルトの、cron.hourlyでanacronを実行するためのcron設定ファイルが cronie-anacronパッケージでインストールされる。
# rpm -ql cronie-anacron /etc/anacrontab /etc/cron.hourly/0anacron /usr/sbin/anacron /usr/share/man/man5/anacrontab.5.gz /usr/share/man/man8/anacron.8.gz /var/spool/anacron /var/spool/anacron/cron.daily /var/spool/anacron/cron.monthly /var/spool/anacron/cron.weekly
/etc/anacrontab:
# more /etc/anacrontab # /etc/anacrontab: configuration file for anacron # See anacron(8) and anacrontab(5) for details. SHELL=/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root # the maximal random delay added to the base delay of the jobs RANDOM_DELAY=45 # the jobs will be started during the following hours only START_HOURS_RANGE=3-22 #period in days delay in minutes job-identifier command 1 5 cron.daily nice run-parts /etc/cron.daily 7 25 cron.weekly nice run-parts /etc/cron.weekly @monthly 45 cron.monthly nice run-parts /etc/cron.monthly
デフォルトでは START_HOURS_RANGE が 3時~22時と非常に広いレンジになっている。
考え方としては、CentOSをインストールしたのが 3時~22時の間であればその間のcron.hourlyにより、最初のanacronジョブが実行される。
23時以降も cron.hourly で起動されるが、START_HOURS_RANGE外なのでジョブは実行されない。
3時になって cron.hourly によりanacronが実行されると、START_HOURS_RANGE内に入ったので、ジョブが実行される。
そのさい、"delay in minutes" で遅延した後に、さらにRANDOM_DELAYで指定した分数の間、ランダムの分で遅延してジョブが実行される。
整理すると、以下のように事実上、anacronが "/etc/cron.daily", "/etc/cron.weekly", "/etc/cron.monthly" を実行している。
/etc/crontab : 空っぽ /etc/cron.d/ 0hourly : 毎時1分に "/etc/cron.hourly" 以下を実行 /etc/cron.hourly/ 0anacron : anacron 実行
→ crond自身は上記まで、つまり "/etc/cron.d/0hourly" 経由で "/etc/cron.hourly/" までがcrondの範囲。
"/etc/cron.(daily|weekly|monthly)" は、以下のようにanacron経由で実行される。
/etc/anacrontab:
... START_HOURS_RANGE=3-22 #period in days delay in minutes job-identifier command 1 5 cron.daily nice run-parts /etc/cron.daily 7 25 cron.weekly nice run-parts /etc/cron.weekly @monthly 45 cron.monthly nice run-parts /etc/cron.monthly
cronie-noanacronをインストールして、代わりにcronie-anacronを削除すれば良いらしい。
yum install cronie-noanacron yum remove cronie-anacron
Minimal Desktop でインストールすると日本語入力が入らない。
基本的な流れとしては "Japanese Support" を groupinstall する。IBusも入るので、設定画面からAnty選択。
Java/文字コードメモ3 に続いたメモ4.
Javaでバイナリデータを文字列として処理する、つまり、バイナリデータ中のASCII文字だけでindexOf()や正規表現が動けば良い場合、byte配列をどのエンコーディングでStringにするのが適切か?
個人的にはISO-8859-1が、0x00 - 0xFFまでひと通り1バイト=1文字でマッピングがされているため、一番安全な解に思える。
重要なのが制御文字や0x80 - 0xFFまでに1バイト=1文字でマッピングされている点。これが無いと、0x80-0xFFのバイト配列→文字列→バイト配列→文字列、と何回か変形した場合に、元に戻らない。
サンプルコード:
参考:
トピックその2:String型とbyte配列の変換って、スレッドセーフか?
基本的に大丈夫だと思うが・・・
なんか、ブロッキングでパフォーマンスが~とかいう話が2006年ごろからまれにあるっぽい。