This discussion is archived
2 Replies Latest reply: Jun 10, 2012 10:38 PM by 941604 RSS

ある件数以上で発生するjdk6のループ処理の不具合について

929851 Newbie
Currently Being Moderated
<環境>
OS:Linux
Javaバージョン:
java version "1.6.0_22-rev"
Java(TM) Platform, Standard Edition for Business (build 1.6.0_22-rev-b05)
Java HotSpot(TM) 64-Bit Server VM (build 17.1-b04, mixed mode)
-----
 上記環境で、javaで作成したバッチを実行していたのですが、以下のループ処理(ロジック①)で処理見込み件数より少ない件数でループが抜けてしまうという現象が発生しました。
※ 処理データ件数が3,000~4,000以上で現象発生。

=== ロジック① ===
// "_rs"は、DBから取得したデータが格納されているResultSetオブジェクト
// "row"は、int型変数で 1 が格納されている。
// "count"は、int型変数で 2147483647 が格納されている。
// "clmcnt"は、int型変数で 6 が格納されている。
int n = 0; // 現在の行番号
int cnt = 0; // 出力した行数
while (_rs.next()) {
     if (++n < row) continue;
     if (++cnt > count) break;
     for (int i = 0; i < clmcnt; i++) {
          任意処理
     }
     任意処理
}

実際の動きの見る為に、上記ループ処理の一部を加工した以下のループ処理(ロジック②)で確認したところ、処理見込み件数 4018件全て処理されるはずが、3374件しか処理されずにループから抜けており、また後程記載するデバッグログを見ると、if文の条件判定式の結果が false にも関わらず、trueの時の処理が実行されていることが分かりました。

=== ロジック② ===
// "_rs"は、DBから取得したデータが格納されているResultSetオブジェクト(データ件数:4018件)
// "row"は、int型変数で 1 が格納されている。
// "count"は、int型変数で 2147483647 が格納されている。
// "clmcnt"は、int型変数で 6 が格納されている。
int n = 0; // 現在の行番号
int cnt = 0; // 出力した行数
while (_rs.next()) {
     if (++n < row) continue;
     // デバッグログとして、以下の内容を出力
     // "判定結果=[" + (++cnt > count) + "], cnt=[" + cnt + "], count=[" + count + "], clmcnt=[" + clmcnt + "]"
     boolean resflag = (cnt > count);
     // デバッグログとして、以下の内容を出力
     // "resflag=[" + resflag + "]"
     if (resflag) {
          // デバッグログとして、以下の内容を出力
          // "[LAST]判定結果=[" + (cnt > count) + "], cnt=[" + cnt + "], count=[" + count + "], resflag=[" + resflag + "], clmcnt=[" + clmcnt + "]"
          break;
     }
     for (int i = 0; i < clmcnt; i++) {
          任意処理
     }
     任意処理
}

=== ロジック②で出力されたデバッグログの抜粋 ===
   :
[DEBUG] 判定結果=[false], cnt=[3373], count=[2147483647], clmcnt=[6]
[DEBUG] resflag=[false]
[DEBUG] 判定結果=[false], cnt=[3374], count=[2147483647], clmcnt=[6]
[DEBUG] resflag=[false]
[DEBUG] [LAST]判定結果=[false], cnt=[3374], count=[2147483647], resflag=[false], clmcnt=[6]

上記に関する内容をWebで調べたところ、jdk7のループ最適化不具合に関する記事の中で、jdk6環境でも-XX:+OptimizeStringConcatか-XX:+AggressiveOptsパラメータを指定すると不具合が発生する旨の記述を見つけたのですが、バッチを実行する際の記述に該当するパラメータは特に指定しておりません。

そこで、このケースは該当パラメータを指定していなくても同じ現象に相当するものなのでしょうか。
もし、同じ現象に相当する場合、jdk6でバグが改修されたバージョンは、幾つになりますでしょうか。
  • 1. Re: ある件数以上で発生するjdk6のループ処理の不具合について
    941604 Newbie
    Currently Being Moderated
    回答にはなりませんが、この現象は jdkではなく Oracleの本体側にあるように思えます。
    PL/SQLで、以下の処理を行なったところ、同様に途中でcontinueがbreakしているようです。
    continueを使わず、以降の処理をif文で分岐させることで対応しました。

    17:01:56 SQL> set serveroutput on
    17:02:09 SQL> create global temporary table testconti
    2 ( ikey number not null )
    3 on commit preserve rows ;

    表が作成されました。

    18:04:55 SQL> begin
    2 for i in 1..30000 loop
    3 insert into testconti ( ikey ) values ( i ) ;
    4 end loop ;
    5 end ;
    6 /

    PL/SQLプロシージャが正常に完了しました。

    18:21:00 SQL> commit ;

    コミットが完了しました。

    18:21:19 SQL> select count(*) from testconti ;

    COUNT(*)
    ----------
    30000

    18:21:33 SQL> declare
    2 icnt int ;
    3 begin
    4 icnt := 0 ;
    5 for irec in (select ikey from testconti) loop
    6 icnt := icnt + 1 ;
    7 if mod( icnt,10 ) = 0 then
    8 continue ;
    9 end if ;
    10 end loop ;
    11 dbms_output.put_line( 'loop end icnt = ' || icnt ) ;
    12 end ;
    13 /
    loop end icnt = 3000

    PL/SQLプロシージャが正常に完了しました。
  • 2. Re: ある件数以上で発生するjdk6のループ処理の不具合について
    941604 Newbie
    Currently Being Moderated
    自己フォローですが、前回の暗黙カーソルだけでなく、
    明示カーソルを使ったFORループでも同様の不具合がありました。

    そこで、明示カーソルでさらにOPENとFETCHを記述する方式を試してみたところ、
    今度は正常に動作しました。
    PL/SQLであればこの方法を採るのが最良ですが、jdkとなると、対応方法は
    「continueを使わない」ということになるのでしょうか。

    14:18:37 SQL> set serveroutput on
    14:18:46 SQL> select count(*) from testconti ;

    COUNT(*)
    ----------
    30000

    14:19:06 SQL> declare
    2 icnt int ;
    3 cursor ctest is select ikey from testconti;
    4 irec ctest%rowtype ;
    5 BEGIN
    6 icnt := 0 ;
    7 for irec in ctest loop
    8 icnt := icnt + 1 ;
    9 if mod( icnt,7 ) = 0 then
    10 continue ;
    11 end if ;
    12 if mod( icnt,100 ) = 0 then
    13 dbms_output.put_line( 'mod 100 icnt = ' || icnt ) ;
    14 end if ;
    15 end loop ;
    16 dbms_output.put_line( 'loop end icnt = ' || icnt ) ;
    17 end ;
    18 /
    mod 100 icnt = 100
    mod 100 icnt = 200
    mod 100 icnt = 300
    mod 100 icnt = 400
    mod 100 icnt = 500
    mod 100 icnt = 600
    mod 100 icnt = 800
    mod 100 icnt = 900
    mod 100 icnt = 1000
    mod 100 icnt = 1100
    mod 100 icnt = 1200
    mod 100 icnt = 1300
    mod 100 icnt = 1500
    mod 100 icnt = 1600
    mod 100 icnt = 1700
    mod 100 icnt = 1800
    mod 100 icnt = 1900
    mod 100 icnt = 2000
    loop end icnt = 2100

    PL/SQLプロシージャが正常に完了しました。

    14:21:37 SQL> declare
    2 icnt int ;
    3 cursor ctest is select ikey from testconti;
    4 irec ctest%rowtype ;
    5 BEGIN
    6 icnt := 0 ;
    7 open ctest ;
    8 loop
    9 fetch ctest into irec ;
    10 exit when ctest%notfound ;
    11 icnt := icnt + 1 ;
    12 if mod( icnt,7 ) = 0 then
    13 continue ;
    14 end if ;
    15 if mod( icnt,100 ) = 0 then
    16 dbms_output.put_line( 'mod 100 icnt = ' || icnt ) ;
    17 end if ;
    18 end loop ;
    19 close ctest ;
    20 dbms_output.put_line( 'loop end icnt = ' || icnt ) ;
    21 end ;
    22 /
    mod 100 icnt = 100
    mod 100 icnt = 200
    mod 100 icnt = 300
    <<略>>
    mod 100 icnt = 29800
    mod 100 icnt = 29900
    mod 100 icnt = 30000
    loop end icnt = 30000

    PL/SQLプロシージャが正常に完了しました。

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points