Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update GraalVM Native Image configuration for 19.2.0. #9515

Merged
merged 1 commit into from
Aug 30, 2019

Conversation

cstancu
Copy link
Contributor

@cstancu cstancu commented Aug 27, 2019

Motivation:

The Netty classes are initialized at build time by default for GraalVM Native Image compilation. This is configured via the --initialize-at-build-time=io.netty option. While this reduces start-up time it can lead to some problems:

  • The class initializer of io.netty.buffer.PooledByteBufAllocator looks at the maximum memory size to compute the size of internal buffers. If the class initializer runs during image generation, then the buffers are sized according to the very large heap size that the image generator uses, and Netty allocates several arrays that are 16 MByte. The fix is to initialize the following 3 classes at run time: io.netty.buffer.PooledByteBufAllocator,io.netty.buffer.ByteBufAllocator,io.netty.buffer.ByteBufUtil. This fix was dependent on a GraalVM Native Image fix that was included in 19.2.0.

  • The class initializer of io.netty.handler.ssl.util.ThreadLocalInsecureRandom needs to be initialized at runtime to ensure that the generated values are trully random and not fixed for each generated image.

  • The class initializers of io.netty.buffer.AbstractReferenceCountedByteBuf and io.netty.util.AbstractReferenceCounted compute field offsets. While the field offset recomputation is necessary for correct execution as a native image these initializers also have logic that depends on the presence/absence of sun.misc.Unsafe, e.g., via the -Dio.netty.noUnsafe=true flag. The fix is to push these initializers to runtime so that the field offset lookups (and the logic depending on them) run at run time. This way no manual substitutions are necessary either.

Modifications:

Add META-INF/native-image configuration files that correctly trigger the inialization of the above classes at run time via --initialize-at-run-time=... flags.

Result:

Fixes the initialisation issues described above for Netty executables built with GraalVM.

@netty-bot
Copy link

Can one of the admins verify this patch?

@cstancu cstancu force-pushed the update-svm-configuration branch from e6578b5 to a4c2157 Compare August 27, 2019 06:54
@normanmaurer
Copy link
Member

@netty-bot test this please

1 similar comment
@normanmaurer
Copy link
Member

@netty-bot test this please

@cstancu cstancu force-pushed the update-svm-configuration branch from a4c2157 to 000ba53 Compare August 27, 2019 17:57
@cstancu
Copy link
Contributor Author

cstancu commented Aug 27, 2019

@normanmaurer I just realized that this needs some more work. I tested it with -Dio.netty.noUnsafe=true and there are some issues when unsafe is disabled. The original field offset computation is done via ReferenceCountUpdater.getUnsafeOffset():

public static long getUnsafeOffset(Class<? extends ReferenceCounted> clz, String fieldName) {
        try {
            if (PlatformDependent.hasUnsafe()) {
                return PlatformDependent.objectFieldOffset(clz.getDeclaredField(fieldName));
            }
        } catch (Throwable ignore) {
            // fall-back
        }
        return -1;
    }

where the -1 value is important. But our field recomputation sets it to a proper offset without checking if unsafe is actually enabled, which can break some assumptions later on.

@cstancu cstancu force-pushed the update-svm-configuration branch from 000ba53 to f5a2c2a Compare August 27, 2019 22:57
@cstancu
Copy link
Contributor Author

cstancu commented Aug 27, 2019

@normanmaurer I updated the PR. Instead of using substitutions I just push the initialization of io.netty.buffer.AbstractReferenceCountedByteBuf and io.netty.util.AbstractReferenceCounted to runtime. This way the field offset lookups (and the -1 logic) run at run time. It works quite nice in this case because no other classes depend on these 2 classes, so pushing their initialization to runtime doesn't trigger a chain reaction.

Note: The need to push the initialization of these classes to run time could be avoided by refactoring the usages of ReferenceCountUpdater.getUnsafeOffset()and exposing the internal calls to UNSAFE.objectFieldOffset(field) to the GraalVM analysis, or, by improving the GraalVM analysis to see through the layers of abstraction.

@normanmaurer
Copy link
Member

@netty-bot test this please

@normanmaurer normanmaurer added this to the 4.1.40.Final milestone Aug 28, 2019
@normanmaurer
Copy link
Member

@cstancu can you please update the pr description / commit message to reflect the latest changes (+ squash)

@normanmaurer
Copy link
Member

@pmlopes PTAL as well

Copy link
Contributor

@pmlopes pmlopes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the extra dependency can go away without affecting the expected behaviour of this PR.

@cstancu cstancu force-pushed the update-svm-configuration branch from f5a2c2a to 8bc1858 Compare August 28, 2019 17:22
@cstancu
Copy link
Contributor Author

cstancu commented Aug 28, 2019

I think this PR is now ready to be merged.

@normanmaurer
Copy link
Member

@netty-bot test this please

@normanmaurer
Copy link
Member

@cstancu can you please update the commit message to match our rule and reflect what is now done in the PR:

https://netty.io/wiki/writing-a-commit-message.html

Motivation:

The Netty classes are initialized at build time by default for GraalVM Native Image compilation. This is configured via the `--initialize-at-build-time=io.netty` option. While this reduces start-up time it can lead to some problems:

 - The class initializer of `io.netty.buffer.PooledByteBufAllocator` looks at the maximum memory size to compute the size of internal buffers. If the class initializer runs during image generation, then the buffers are sized according to the very large heap size that the image generator uses, and Netty allocates several arrays that are 16 MByte. The fix is to initialize the following 3 classes at run time: `io.netty.buffer.PooledByteBufAllocator,io.netty.buffer.ByteBufAllocator,io.netty.buffer.ByteBufUtil`. This fix was dependent on a GraalVM Native Image fix that was included in 19.2.0.

 - The class initializer of `io.netty.handler.ssl.util.ThreadLocalInsecureRandom` needs to be initialized at runtime to ensure that the generated values are trully random and not fixed for each generated image.

 - The class initializers of `io.netty.buffer.AbstractReferenceCountedByteBuf` and `io.netty.util.AbstractReferenceCounted` compute field offsets. While the field offset recomputation is necessary for correct execution as a native image these initializers also have logic that depends on the presence/absence of `sun.misc.Unsafe`, e.g., via the `-Dio.netty.noUnsafe=true` flag. The fix is to push these initializers to runtime so that the field offset lookups (and the logic depending on them) run at run time. This way no manual substitutions are necessary either.
 
Modifications:

Add `META-INF/native-image` configuration files that correctly trigger the inialization of the above classes at run time via `--initialize-at-run-time=...` flags.
 
Result:

Fixes the initialisation issues described above for Netty executables built with GraalVM.
@cstancu cstancu force-pushed the update-svm-configuration branch from 8bc1858 to 3cafc67 Compare August 28, 2019 23:29
@cstancu
Copy link
Contributor Author

cstancu commented Aug 28, 2019

All done!

@normanmaurer
Copy link
Member

@netty-bot test this please

@normanmaurer
Copy link
Member

@cstancu thanks a lot!

normanmaurer pushed a commit that referenced this pull request Aug 30, 2019
Motivation:

The Netty classes are initialized at build time by default for GraalVM Native Image compilation. This is configured via the `--initialize-at-build-time=io.netty` option. While this reduces start-up time it can lead to some problems:

 - The class initializer of `io.netty.buffer.PooledByteBufAllocator` looks at the maximum memory size to compute the size of internal buffers. If the class initializer runs during image generation, then the buffers are sized according to the very large heap size that the image generator uses, and Netty allocates several arrays that are 16 MByte. The fix is to initialize the following 3 classes at run time: `io.netty.buffer.PooledByteBufAllocator,io.netty.buffer.ByteBufAllocator,io.netty.buffer.ByteBufUtil`. This fix was dependent on a GraalVM Native Image fix that was included in 19.2.0.

 - The class initializer of `io.netty.handler.ssl.util.ThreadLocalInsecureRandom` needs to be initialized at runtime to ensure that the generated values are trully random and not fixed for each generated image.

 - The class initializers of `io.netty.buffer.AbstractReferenceCountedByteBuf` and `io.netty.util.AbstractReferenceCounted` compute field offsets. While the field offset recomputation is necessary for correct execution as a native image these initializers also have logic that depends on the presence/absence of `sun.misc.Unsafe`, e.g., via the `-Dio.netty.noUnsafe=true` flag. The fix is to push these initializers to runtime so that the field offset lookups (and the logic depending on them) run at run time. This way no manual substitutions are necessary either.
 
Modifications:

Add `META-INF/native-image` configuration files that correctly trigger the inialization of the above classes at run time via `--initialize-at-run-time=...` flags.
 
Result:

Fixes the initialisation issues described above for Netty executables built with GraalVM.
@cstancu
Copy link
Contributor Author

cstancu commented Aug 30, 2019

@normanmaurer thank you for your patience getting this PR in a good shape!

@normanmaurer
Copy link
Member

@cstancu no worries.. we love contributions. So let em keep coming ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants