9.1 アプリケーション実装時の注意事項
ここでは,アプリケーション実装時のJDKに関する注意事項について説明します。
-
URLClassLoaderでのクラスのローディングについて
URLClassLoader経由でjarファイルからクラスをローディングする場合,ローディング処理中にJavaVMの内部で作成されるオブジェクトが,JavaVM終了時まで削除されないことがあります。適宜,Javaアプリケーションを再起動してください。
-
メソッド長の上限について
JavaVMの仕様によって,1メソッドのバイトコードは,64キロバイト以内にする必要があります。
64キロバイトを超えると,クラスファイル生成時にエラーとなるか,またはクラスのロード時にjava.lang.LinkageError例外が発生します。
また,64キロバイト以内であっても,非常に複雑で行数が多い場合は,次のような弊害が発生することがあります。
-
GC処理の実行に非常に時間が掛かる。
-
JITコンパイルに非常に時間が掛かる。
-
JITコンパイルに非常に多くのメモリを消費する。
さらに,ローカル変数情報出力機能が有効な場合は,次の弊害も発生することがあります。
-
拡張スレッドダンプの出力に時間が掛かる。
-
スレッドスタックトレースの取得に時間が掛かる。
-
例外発生時の例外オブジェクト生成処理に時間が掛かる。
そのため,Javaソース上の1メソッドの行数は,コメントや空行を除いて,およそ500行以内にすることをお勧めします。
-
-
ファイルディスクリプタのクローズについて
java.lang.Runtime.exec(),およびjava.lang.ProcessBuilder.start()で起動した子プロセスは,Process.getInputStream,getErrorStream,またはgetOutputStreamのそれぞれのメソッドで取り出したストリームを通じてプロセス間通信をします。
親プロセスでは,これらのメソッドを使わない場合でも3つのファイルディスクリプタを消費することに注意してください。
ファイルディスクリプタをクローズするのは次の場合です。
-
Processオブジェクトのファイナライズが完了した場合
-
Process.destroy()を呼び出した場合
-
これらストリームに対して明示的にclose()メソッドを呼び出した場合
-
-
システムプロパティjava.library.pathについて
システムプロパティjava.library.pathには,ユーザのネイティブライブラリの検索パスを指定します。デフォルト値を次に示します。
JDK下のネイティブライブラリのディレクトリ,環境変数LD_LIBRARY_PATHの設定値,"/usr/lib64:/lib64:/lib:/usr/lib"
-
システムプロパティjava.ext.dirsについて(JDK8の場合)
システムプロパティjava.ext.dirsには,通常のアプリケーションクラスよりも優先してロードするクラスを含むjarファイルを配置するディレクトリを指定します。デフォルト値を次に示します。
/opt/Cosminexus/jdk/jre/lib/ext
-
java.net.Socket.connect()のタイムアウトについて
java.net.Socket.connect()でのソケットの接続が,OSに設定されているTCP通信のタイムアウト値によってタイムアウトすることがあります。TCP通信のタイムアウト値になると,java.net.Socket.connect()に指定したタイムアウト値よりも前でも,タイムアウト値を指定していないときでも,接続がタイムアウトします。
TCP通信のタイムアウト値でタイムアウトした場合,次の詳細メッセージを含むjava.net.ConnectException例外がスローされます。
- メッセージの内容
-
Connection timed out: connect [errno=10060, syscall=select]
TCP通信のタイムアウトについては,OSのドキュメントを参照してください。
-
クラスロードのタイミングについて
Javaでのクラスファイルのメモリへの読み込み(クラスロード処理)は,プログラムの実行中に,そのクラスが初めて必要となったタイミングで実行されます。そのため,ある処理の初回実行でクラスロードの回数が多くなると,2回目以降と比較して,処理時間が長くなることがあります。この場合,処理の実行に必要なクラスを事前にロードすることで,処理時間が改善します。
-
異なるOS間で発生する文字化けについて
文字コードのUnicodeへのマッピングは,OSによって異なることがあります。このため,次の場合に文字化けが発生することがあります。文字化けが発生する代表的な文字には,—,〜,‖,−,¢,£,¬があります。
-
UTF-8などのUnicodeのエンコーディングを使用して,異なるOS間で上記文字データを受け渡す場合
-
上記文字を含む文字列リテラルがあるソースプログラムから作成したクラスファイルを,異なるOS上でリコンパイルしないでそのまま実行する場合
-
-
Java APIで使用するファイルについて
Java APIで使用するポリシーファイルや,ログイン構成ファイルなどのコンフィグレーションファイルは,UTF-8エンコーディング方式でエンコーディングする必要があります。
-
JNI関数で文字列操作をする場合のUTF-8エンコーディングについて
JNIの場合,次の文字列操作をする関数で使用するエンコーディングは,標準UTF-8ではなく,Modified UTF-8です。
-
NewStringUTF
-
GetStringUTFLength
-
GetStringUTFChars
-
ReleaseStringUTFChars
-
GetStringUTFRegion
-
-
if文の判定でジャンプできる長さの上限について
JavaVMの仕様によって,if文の判定でジャンプできる長さは32キロバイトまでです。ジャンプ先が32キロバイトを超える場合は,java.lang.LinkageErrorとなります。
-
java.nio.channels.FileChannelクラスのmap,transferFrom,およびtransferToメソッドについて
mapメソッドでは,2ギガバイトを超えるファイルはサポートしていません。
また,transferFromおよびtransferToメソッドでのcount指定では,2ギガバイト以上の値はサポートしていません。
-
スレッドのスタックサイズについて
JNI関数AttachCurrentThread()などを使用して,ネイティブスレッドをJavaVMに接続する場合は,-Xssオプションの指定値よりも大きなスタックサイズのスレッドで接続してください。
-
JavaプロセスがSSL/TLS通信のクライアントとして動作する場合の注意点について
SSL/TLSを使った通信相手のプロトコルを変更した場合,通信に失敗するおそれがあります。
通信相手(サーバ)側で使用できるSSL/TLSプロトコルバージョンを変更したあと,クライアント側のJavaプロセスを再起動しないでSSL/TLSを使った通信をしようとした場合,クライアント側のJavaプロセスで例外が発生して通信に失敗するおそれがあります。
これは,既知の接続先に対して以前接続に成功したプロトコルの情報がクライアント側に残っていると,そのプロトコルバージョンでSSL/TLS通信を試みる,という挙動によるものです。
通信相手が使用できるSSL/TLSプロトコルバージョンを変更する場合は,クライアント側のJavaプロセスを再起動してください。
また,通信相手(サーバ)側で使用できるSSL/TLSプロトコルバージョンを変更したあと,クライアント側のJavaプロセスを再起動しないで通信が失敗した場合は,対象のJavaプロセスを再起動してください。
-
forkシステムコールについて
JNIやJVMTIで呼び出されるネイティブメソッド,またはネイティブコードで,fork()システムコール発行だけで現行プロセスのコピーの子プロセスを生成,または実行した場合,その親子プロセスの動作は保証できません。fork()システムコール発行による子プロセス生成後は,必ずexec()システムコールで新規プログラムをローディングしてから起動してください。また,Java環境下で子プロセスを生成する場合には,Javaクラスライブラリのjava.lang.Runtime.exec()メソッドを使用することをお勧めします。
-
シグナルについて
JNIやJVMTIで呼び出されるネイティブメソッド,またはネイティブコードで,次のシグナルに対してシグナルハンドラを登録した場合,動作は保証しません。
SIGHUP,SIGINT,SIGQUIT,SIGILL,SIGFPE,SIGBUS,SIGSEGV,SIGPIPE,SIGTERM,SIGUSR2,SIGCHLD,SIGXCPU,SIGXFSZ,(_SIGRTMAX-2)番のシグナル
-
システムライブラリ関数やシステムコール呼び出し中のシグナル受信について
システムライブラリ関数やシステムコール呼び出し中に,次のシグナルを受信することがあります。
SIGHUP,SIGINT,SIGQUIT,SIGILL,SIGFPE,SIGBUS,SIGSEGV,SIGPIPE,SIGTERM,SIGUSR2,SIGCHLD,SIGXCPU,SIGXFSZ,(_SIGRTMAX-2)番のシグナル
システムライブラリ関数やシステムコールを呼び出す場合,該当する関数の処理が,これらのシグナル受信によって中断されて,エラーリターン(errno値にEINTRが設定されるなど)することがあります。この場合は,適切な処置(例えば,再実行など)を実施してください。
-
JNIプログラム中で使用できないライブラリについて
JavaVM内で管理している情報が更新されて不正な情報となる場合があるため,JNIプログラム中で次のライブラリは使用できません。
-
setjmp()
-
longjmp()
-
_setjmp()
-
_longjmp()
-
sigsetjmp()
-
siglongjmp()
-
-
Windowsとの文字データの受け渡しについて
Windowsとの文字データの受け渡しをする場合には,エンコーディングにMS932,windows-31j,cswindows31jのどれかを指定してください。
Shift_JISやSJISを指定すると,次の文字が不正な文字コードに変換されます。
-
生成できるスレッド数の上限値について
システム全体で生成できるスレッド数の上限は,/proc/sys/kernel/threads-maxです。また,1ユーザ当たりの生成できるスレッド数の上限は,/etc/security/limits.confのnprocの値(ulimit -uと同じ)となります。システムに応じて,これらの値を調整してください。
-
通知待ちのモニタ数について
JVMTIインタフェースを使用して取得した,通知待ちのモニタ情報が不正となる場合があります。
-
半角カタカナについて
JIS X 201で制定されている文字のうち,半角カタカナはJavaSEのAPIを使用したGUIの実装では利用できません。
-
java.security.SecureRandomクラスについて
/dev/randomファイルがあるプラットフォームでは,SecureRandomクラスの一部のAPIで/dev/randomファイルからOSが生成した乱数を取得します。/dev/randomファイルから乱数を取得するのは,次の条件がすべて重なる場合です。
-
java.security.egdプロパティまたはjava.securityファイル中のsecurerandom.sourceにfile:/dev/random/を指定する
-
SecureRandomクラスのgenerateSeed()メソッドまたはgetSeed()メソッドを呼び出す
OSの乱数生成速度には限度があります。このため,短い間隔で乱数の取得処理を実行すると,OSによる乱数生成完了まで処理が完了しないので注意してください。
-
-
java.io.tmpdirプロパティについて
java.io.tmpdirプロパティには,書き込み権限があり,かつ存在するディレクトリを指定してください。java.io.tmpdirプロパティの初期値は/tmpです。
また,Java RMIの動的クラスローディング機能やJava APIのjava.io.File.createTempFile()では,java.io.tmpdirプロパティで指定されたディレクトリに一時ファイルを作成します。これらの機能を正常に動作させるため,JavaVMプロセス起動中は一時ファイルの作成先を削除しないでください。
-
デフォルトエンコーディングについて
アプリケーションサーバのデフォルトエンコーディングを次に示します。
LANGがCまたはPOSIXのとき:US-ASCII(別名ASCII)
LANGがjp_JP,jp_JP.eucJP,ja_JP.ujis,japanese,japanese.eucのどれかのとき:x-euc-jp-linux(別名EUC_JP_LINUX)
LANGがja_JP.utf8のとき:UTF-8(別名UTF8)