Derby
  1. Derby
  2. DERBY-176

Derby throws ERROR XBCM1: Java linkage error thrown during load of generated class org.apache.derby.exe.aced07c066x0102xca87x3319x00004aa5686e1 during execution of large query

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 10.0.2.0, 10.0.2.1, 10.0.2.2, 10.1.1.0
    • Fix Version/s: 10.1.3.2, 10.2.1.6, 10.3.1.4
    • Component/s: SQL
    • Labels:
      None

      Description

      When executing a large query or oather large operations, Derby throws a java linkage exception.
      This is because the generated byte code exceeds the JVM limits for method sizes constant pool entries etc, the amount of code in a conditional etc. The attached repro demonstrates the problem but the problem can also occur for other operations that generate lots of byte code. The repro is just a new functional test,
      so should be copied to derbyTesting/functionTests/lang/largeCodeGen.java and run like
      java -Djvmflags=-Xmx512M org.apache.derbyTesting.harness.RunTest lang/largeCodeGen

      When this problem is fixed additional scenarios should be added to this test.

      ERROR XBCM1: Java linkage error thrown during load of
      generated class org.apache.derby.exe.aced07c066x0102xca87x3319x00004aa5686e1.
      at org.apache.derby.iapi.error.StandardException.newException(StandardException.java:315)
      at org.apache.derby.impl.services.reflect.DatabaseClasses.loadGeneratedClass(DatabaseClasses.java:162)
      at org.apache.derby.impl.services.bytecode.GClass.getGeneratedClass(GClass.java:59)
      at org.apache.derby.impl.sql.compile.ExpressionClassBuilder.getGeneratedClass(ExpressionClassBuilder.java:920)
      at org.apache.derby.impl.sql.compile.StatementNode.generate(StatementNode.java:270)
      at org.apache.derby.impl.sql.GenericStatement.prepMinion(GenericStatement.java:432)
      at org.apache.derby.impl.sql.GenericStatement.prepare(GenericStatement.java:107)
      at org.apache.derby.impl.sql.conn.GenericLanguageConnectionContext.prepareInternalStatement(GenericLanguageConnectionContext.java:688)
      at org.apache.derby.impl.jdbc.EmbedPreparedStatement.<init>(EmbedPreparedStatement.java:118)
      at org.apache.derby.impl.jdbc.EmbedPreparedStatement20.<init>(EmbedPreparedStatement20.java:82)
      at org.apache.derby.impl.jdbc.EmbedPreparedStatement30.<init>(EmbedPreparedStatement30.java:62)
      at org.apache.derby.jdbc.Driver30.newEmbedPreparedStatement(Driver30.java:92)
      at org.apache.derby.impl.jdbc.EmbedConnection.prepareStatement(EmbedConnection.java:675)
      at org.apache.derby.impl.jdbc.EmbedConnection.prepareStatement(EmbedConnection.java:519)
      at org.apache.derbyTesting.functionTests.tests.lang.largeCodeGen.main(largeCodeGen.java:86)
      Exception in thread "main"

      1. largeCodeGen.java
        4 kB
        Kathey Marsden

        Issue Links

          Activity

          Hide
          Jack Klebanoff added a comment -

          I think that there are 3 approaches to solving the problem of the Derby code generator exceeding JVM class size limits:
          1. Reduce the amount of code that is generated.
          2. Split oversized generated methods into multiple methods.
          3. Split oversized generated classes into multiple classes.
          We may want (need) to use all three.

          We may be able to reduce the amount of generated code by looking for common sub-expressions in the optimized query tree and putting the code for them in separate methods. Or we may be able to reuse intermediate result sets. Perhaps just sharing code for equivalent ProjectResrictNodes and SubQueryNodes would be enough.

          Splitting generated classes may be difficult. Derby assumes that each statement only generates one class. It might be possible to put the byte code for the overflow classes into saved objects of the generated ExecPreparedStatement. The main generated class would load the byte code when needed.

          Show
          Jack Klebanoff added a comment - I think that there are 3 approaches to solving the problem of the Derby code generator exceeding JVM class size limits: 1. Reduce the amount of code that is generated. 2. Split oversized generated methods into multiple methods. 3. Split oversized generated classes into multiple classes. We may want (need) to use all three. We may be able to reduce the amount of generated code by looking for common sub-expressions in the optimized query tree and putting the code for them in separate methods. Or we may be able to reuse intermediate result sets. Perhaps just sharing code for equivalent ProjectResrictNodes and SubQueryNodes would be enough. Splitting generated classes may be difficult. Derby assumes that each statement only generates one class. It might be possible to put the byte code for the overflow classes into saved objects of the generated ExecPreparedStatement. The main generated class would load the byte code when needed.
          Hide
          Kathey Marsden added a comment -

          large code generation test

          Show
          Kathey Marsden added a comment - large code generation test
          Hide
          Daniel John Debrunner added a comment -

          Kathey Marsden and myself made a number of changes in IBM's Cloudscape 5.1 codeline to make progress in this area and address an urgent customer issue. They fall into Jack's 1) and 2) categories, generate less code and split methods up into multiple methods. In addition some simple checks are made to ensure the structure of the class file is not corrupted by exceeding limits. These checks are made before handing the class off to the class loader/verifier.

          I'll migrate these changes to Derby's trunk and add notes for them in this bug.

          Show
          Daniel John Debrunner added a comment - Kathey Marsden and myself made a number of changes in IBM's Cloudscape 5.1 codeline to make progress in this area and address an urgent customer issue. They fall into Jack's 1) and 2) categories, generate less code and split methods up into multiple methods. In addition some simple checks are made to ensure the structure of the class file is not corrupted by exceeding limits. These checks are made before handing the class off to the class loader/verifier. I'll migrate these changes to Derby's trunk and add notes for them in this bug.
          Hide
          Daniel John Debrunner added a comment -

          svn revision 160426
          Added checks when the generated class file's format is created to ensure the structure is not corrupted by writing truncated values. E.g. a constant pool count N with N >65535 now will throw an error rather than generate a class file with an apparent constant pool count of N % 65535.

          Show
          Daniel John Debrunner added a comment - svn revision 160426 Added checks when the generated class file's format is created to ensure the structure is not corrupted by writing truncated values. E.g. a constant pool count N with N >65535 now will throw an error rather than generate a class file with an apparent constant pool count of N % 65535.
          Hide
          Daniel John Debrunner added a comment -

          svn revision 160429

          Reduce the number of constant pool entries in code generation by removing some uses of java fields.

          In the binary operator nodes code is generated like

          <left>.method(<left>, <right>)

          where <left> and <right> are arbitary expressions.
          In this case <left> must only be evaluated once and the existing implementation used Java fields.

          DataValueDescriptor f34;

          f34 = <left>;

          f34.method(f34, <right>)

          The issue is that a field is not required, really a local variable is, and a field and its use will create three unique constant pool entries. However local variables are not supported by the byte code compiler currently.

          The stack based code generation allows the use of the stack through MethodBuilder.dup() method to evaluate <left> once and re-use the result.
          E.g.

          <left>
          dup
          <right>

          leaves the stack as left,left,right which is the correct order for such a method call. The use of MethodBuilder.swap() allows the same pattern when <right> needs to be re-used in <right>.method(<left>, <right>)

          <right>
          dup
          <left>
          swap

          leaves right,left,right on the stack.

          Show
          Daniel John Debrunner added a comment - svn revision 160429 Reduce the number of constant pool entries in code generation by removing some uses of java fields. In the binary operator nodes code is generated like <left>.method(<left>, <right>) where <left> and <right> are arbitary expressions. In this case <left> must only be evaluated once and the existing implementation used Java fields. DataValueDescriptor f34; f34 = <left>; f34.method(f34, <right>) The issue is that a field is not required, really a local variable is, and a field and its use will create three unique constant pool entries. However local variables are not supported by the byte code compiler currently. The stack based code generation allows the use of the stack through MethodBuilder.dup() method to evaluate <left> once and re-use the result. E.g. <left> dup <right> leaves the stack as left,left,right which is the correct order for such a method call. The use of MethodBuilder.swap() allows the same pattern when <right> needs to be re-used in <right>.method(<left>, <right>) <right> dup <left> swap leaves right,left,right on the stack.
          Hide
          Daniel John Debrunner added a comment -

          A modified version of Kathey's test case has been committed into the trunk

          Show
          Daniel John Debrunner added a comment - A modified version of Kathey's test case has been committed into the trunk
          Hide
          Kathey Marsden added a comment -

          Not able to work on this right now so unassigning myself

          Show
          Kathey Marsden added a comment - Not able to work on this right now so unassigning myself
          Hide
          Daniel John Debrunner added a comment -

          DERBY-176 and DERBY-732 are most likely both instances where a SQL query blows some limit of the class file format.

          Show
          Daniel John Debrunner added a comment - DERBY-176 and DERBY-732 are most likely both instances where a SQL query blows some limit of the class file format.
          Hide
          Daniel John Debrunner added a comment -

          Other commits for this issue:

          160444 - Add pop() method to byte code compiler. (incorrect makred for Derby-167)

          160580 - Change generate fields to use the same name space as generated methods to reduce the number of constant pool entries created.

          160631 -Automatically handle a generated method exceeding the
          byte code limit of 65,535 in limited cases of a method
          with no parameters and whose stack depth drops to zero
          during code generation. Solution is to generate sub methods
          with the same signature and pass control to those methods.
          The sub methods are private and hidden to any callers, who
          just interact with the single top-level method.

          160932 - Reduce the number of code instructions generated for a putField() followed by an endStatement()
          by providing a setField() method that sets a field with the top value of the stack but does not
          leave the field's contents on the stack. The use of putField() mimicing the Java language field=value;
          lead to many cases where the value on the stack was not required and immediately popped using endStatement().
          This lead to two extra instructions per field assignment (dup and pop), providing a setField() removes the
          need for the endStatement() as it never performs the dup.

          Show
          Daniel John Debrunner added a comment - Other commits for this issue: 160444 - Add pop() method to byte code compiler. (incorrect makred for Derby-167) 160580 - Change generate fields to use the same name space as generated methods to reduce the number of constant pool entries created. 160631 -Automatically handle a generated method exceeding the byte code limit of 65,535 in limited cases of a method with no parameters and whose stack depth drops to zero during code generation. Solution is to generate sub methods with the same signature and pass control to those methods. The sub methods are private and hidden to any callers, who just interact with the single top-level method. 160932 - Reduce the number of code instructions generated for a putField() followed by an endStatement() by providing a setField() method that sets a field with the top value of the stack but does not leave the field's contents on the stack. The use of putField() mimicing the Java language field=value; lead to many cases where the value on the stack was not required and immediately popped using endStatement(). This lead to two extra instructions per field assignment (dup and pop), providing a setField() removes the need for the endStatement() as it never performs the dup.
          Hide
          Kathey Marsden added a comment -

          While we would like to continue to make progress on DERBY-176, there will likely always be some upper bound on the complexity of queries. I think a new error message would help a lot in clarifying necessary action and setting proper expectations for users.

          How about this?

          Statement too complex. The resulting java byte code exceeds java class file format limit(s). Try rewriting the query to remove complexity. Eliminating many duplicate expressions or breaking up the query and storing interim results in a temporary table can often help resolve this error. method:fillResultSet code_length (68224 > 6
          5536) in generated class org.apache.derby.exe.ace5214067x0108x0775x104fxfffff2d773e3c

          Show
          Kathey Marsden added a comment - While we would like to continue to make progress on DERBY-176 , there will likely always be some upper bound on the complexity of queries. I think a new error message would help a lot in clarifying necessary action and setting proper expectations for users. How about this? Statement too complex. The resulting java byte code exceeds java class file format limit(s). Try rewriting the query to remove complexity. Eliminating many duplicate expressions or breaking up the query and storing interim results in a temporary table can often help resolve this error. method:fillResultSet code_length (68224 > 6 5536) in generated class org.apache.derby.exe.ace5214067x0108x0775x104fxfffff2d773e3c
          Hide
          Daniel John Debrunner added a comment -

          I have code that traps most limit execeed errors and throws a statement too complex error. I will submit this soon, I will check it in with wording similar to yours:

          Statement too complex. Try rewriting the query to remove complexity. Eliminating many duplicate expressions or breaking up the query and storing interim results in a temporary table can often help resolve this error.

          I don't think the application developer cares that the Java class limits have been execeed, that's an implementation detail. The exception will have a nested/chained exception that has the specific info on the class file.

          Coding this exception actually help me find the cause for DERBY-732, the method is not exceeding any limit but a branch offset with the method is greater than 32k. Branch offsets are a 16bit signed value, most other 16bit values in the class file are unsigned values.

          Show
          Daniel John Debrunner added a comment - I have code that traps most limit execeed errors and throws a statement too complex error. I will submit this soon, I will check it in with wording similar to yours: Statement too complex. Try rewriting the query to remove complexity. Eliminating many duplicate expressions or breaking up the query and storing interim results in a temporary table can often help resolve this error. I don't think the application developer cares that the Java class limits have been execeed, that's an implementation detail. The exception will have a nested/chained exception that has the specific info on the class file. Coding this exception actually help me find the cause for DERBY-732 , the method is not exceeding any limit but a branch offset with the method is greater than 32k. Branch offsets are a 16bit signed value, most other 16bit values in the class file are unsigned values.
          Hide
          Daniel John Debrunner added a comment -

          Changes for DERBY-766 and under this bug ensured that a "Query too complex" exception is thrown if the resulting generated class will not be loadable by the JVM due to exceeding limits.

          Show
          Daniel John Debrunner added a comment - Changes for DERBY-766 and under this bug ensured that a "Query too complex" exception is thrown if the resulting generated class will not be loadable by the JVM due to exceeding limits.
          Hide
          Kathey Marsden added a comment -

          I received a request to backport this change to 10.1. I will be looking at doing so incrementally so their are multiple checkins to 10.1 rather than one big change. Please let me know if you have any concerns.

          Show
          Kathey Marsden added a comment - I received a request to backport this change to 10.1. I will be looking at doing so incrementally so their are multiple checkins to 10.1 rather than one big change. Please let me know if you have any concerns.
          Hide
          Kathey Marsden added a comment -

          Reopen to backport to 10.1

          Show
          Kathey Marsden added a comment - Reopen to backport to 10.1
          Hide
          Kathey Marsden added a comment -

          After merging change 377609, I see a different error at 99,000 parameters which causes lose of the connection instead of the query too complex message. Below is the trace. I think this is an acceptable difference for 10.1,
          since this limit is unlikely to be reached and the previous limit is 3400. If this is acceptable I will just change the largeCodeGenTest to open a new connection after getting this failure at 99,000 parameters. up to 98,000 is fine.

          java.lang.ClassCastException: org.apache.derby.iapi.services.classfile.CONSTANT_Integer_info

          at org.apache.derby.impl.services.bytecode.CodeChunk.getTypeDescriptor(CodeChunk.java:978)

          at org.apache.derby.impl.services.bytecode.CodeChunk.getVariableStackDelta(CodeChunk.java:1030)

          at org.apache.derby.impl.services.bytecode.CodeChunk.stackWordDelta(CodeChunk.java:964)

          at org.apache.derby.impl.services.bytecode.CodeChunk.findMaxStack(CodeChunk.java:926)

          at org.apache.derby.impl.services.bytecode.CodeChunk.complete(CodeChunk.java:781)

          at org.apache.derby.impl.services.bytecode.BCMethod.complete(BCMethod.java:237)

          at org.apache.derby.impl.sql.compile.ProjectRestrictNode.generateMinion(ProjectRestrictNode.java:1437)

          at org.apache.derby.impl.sql.compile.ProjectRestrictNode.generate(ProjectRestrictNode.java:1249)

          at org.apache.derby.impl.sql.compile.ProjectRestrictNode.generateMinion(ProjectRestrictNode.java:1300)

          at org.apache.derby.impl.sql.compile.ProjectRestrictNode.generate(ProjectRestrictNode.java:1249)

          at org.apache.derby.impl.sql.compile.ScrollInsensitiveResultSetNode.generate(ScrollInsensitiveResultSetNode.java:109)

          at org.apache.derby.impl.sql.compile.ReadCursorNode.generate(ReadCursorNode.java:118)

          at org.apache.derby.impl.sql.compile.CursorNode.generate(CursorNode.java:546)

          at org.apache.derby.impl.sql.compile.StatementNode.generate(StatementNode.java:232)

          at org.apache.derby.impl.sql.GenericStatement.prepMinion(GenericStatement.java:468)

          at org.apache.derby.impl.sql.GenericStatement.prepare(GenericStatement.java:107)

          at org.apache.derby.impl.sql.conn.GenericLanguageConnectionContext.prepareInternalStatement(GenericLanguageConnectionContext.java:704)

          at org.apache.derby.impl.jdbc.EmbedPreparedStatement.<init>(EmbedPreparedStatement.java:121)

          at org.apache.derby.impl.jdbc.EmbedPreparedStatement20.<init>(EmbedPreparedStatement20.java:82)

          at org.apache.derby.impl.jdbc.EmbedPreparedStatement30.<init>(EmbedPreparedStatement30.java:62)

          at org.apache.derby.jdbc.Driver30.newEmbedPreparedStatement(Driver30.java:92)

          at org.apache.derby.impl.jdbc.EmbedConnection.prepareStatement(EmbedConnection.java:682)

          at org.apache.derby.impl.jdbc.EmbedConnection.prepareStatement(EmbedConnection.java:526)

          at org.apache.derbyTesting.functionTests.tests.lang.largeCodeGen.checkT0Query(largeCodeGen.java:97)

          at org.apache.derbyTesting.functionTests.tests.lang.largeCodeGen.testInClause(largeCodeGen.java:199)

          at org.apache.derbyTesting.functionTests.tests.lang.largeCodeGen.testInClause(largeCodeGen.java:177)

          at org.apache.derbyTesting.functionTests.tests.lang.largeCodeGen.main(largeCodeGen.java:50)

          Show
          Kathey Marsden added a comment - After merging change 377609, I see a different error at 99,000 parameters which causes lose of the connection instead of the query too complex message. Below is the trace. I think this is an acceptable difference for 10.1, since this limit is unlikely to be reached and the previous limit is 3400. If this is acceptable I will just change the largeCodeGenTest to open a new connection after getting this failure at 99,000 parameters. up to 98,000 is fine. java.lang.ClassCastException: org.apache.derby.iapi.services.classfile.CONSTANT_Integer_info at org.apache.derby.impl.services.bytecode.CodeChunk.getTypeDescriptor(CodeChunk.java:978) at org.apache.derby.impl.services.bytecode.CodeChunk.getVariableStackDelta(CodeChunk.java:1030) at org.apache.derby.impl.services.bytecode.CodeChunk.stackWordDelta(CodeChunk.java:964) at org.apache.derby.impl.services.bytecode.CodeChunk.findMaxStack(CodeChunk.java:926) at org.apache.derby.impl.services.bytecode.CodeChunk.complete(CodeChunk.java:781) at org.apache.derby.impl.services.bytecode.BCMethod.complete(BCMethod.java:237) at org.apache.derby.impl.sql.compile.ProjectRestrictNode.generateMinion(ProjectRestrictNode.java:1437) at org.apache.derby.impl.sql.compile.ProjectRestrictNode.generate(ProjectRestrictNode.java:1249) at org.apache.derby.impl.sql.compile.ProjectRestrictNode.generateMinion(ProjectRestrictNode.java:1300) at org.apache.derby.impl.sql.compile.ProjectRestrictNode.generate(ProjectRestrictNode.java:1249) at org.apache.derby.impl.sql.compile.ScrollInsensitiveResultSetNode.generate(ScrollInsensitiveResultSetNode.java:109) at org.apache.derby.impl.sql.compile.ReadCursorNode.generate(ReadCursorNode.java:118) at org.apache.derby.impl.sql.compile.CursorNode.generate(CursorNode.java:546) at org.apache.derby.impl.sql.compile.StatementNode.generate(StatementNode.java:232) at org.apache.derby.impl.sql.GenericStatement.prepMinion(GenericStatement.java:468) at org.apache.derby.impl.sql.GenericStatement.prepare(GenericStatement.java:107) at org.apache.derby.impl.sql.conn.GenericLanguageConnectionContext.prepareInternalStatement(GenericLanguageConnectionContext.java:704) at org.apache.derby.impl.jdbc.EmbedPreparedStatement.<init>(EmbedPreparedStatement.java:121) at org.apache.derby.impl.jdbc.EmbedPreparedStatement20.<init>(EmbedPreparedStatement20.java:82) at org.apache.derby.impl.jdbc.EmbedPreparedStatement30.<init>(EmbedPreparedStatement30.java:62) at org.apache.derby.jdbc.Driver30.newEmbedPreparedStatement(Driver30.java:92) at org.apache.derby.impl.jdbc.EmbedConnection.prepareStatement(EmbedConnection.java:682) at org.apache.derby.impl.jdbc.EmbedConnection.prepareStatement(EmbedConnection.java:526) at org.apache.derbyTesting.functionTests.tests.lang.largeCodeGen.checkT0Query(largeCodeGen.java:97) at org.apache.derbyTesting.functionTests.tests.lang.largeCodeGen.testInClause(largeCodeGen.java:199) at org.apache.derbyTesting.functionTests.tests.lang.largeCodeGen.testInClause(largeCodeGen.java:177) at org.apache.derbyTesting.functionTests.tests.lang.largeCodeGen.main(largeCodeGen.java:50)
          Hide
          Kathey Marsden added a comment -

          Merged for this issue:
          354826,358605,377609,378383

          Also merged fixes for
          DERBY-740, DERBY-739, DERBY-738, DERBY-921, DERBY-742, DERBY-766
          which were needed to complete change.

          Show
          Kathey Marsden added a comment - Merged for this issue: 354826,358605,377609,378383 Also merged fixes for DERBY-740 , DERBY-739 , DERBY-738 , DERBY-921 , DERBY-742 , DERBY-766 which were needed to complete change.

            People

            • Assignee:
              Daniel John Debrunner
              Reporter:
              Kathey Marsden
            • Votes:
              1 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development