oracle / graal

GraalVM compiles Java applications into native executables that start instantly, scale fast, and use fewer compute resources 🚀
https://www.graalvm.org
Other
20.28k stars 1.63k forks source link

[native-image] building swing application fails #860

Open horvathandras opened 5 years ago

horvathandras commented 5 years ago

I'm trying to create a native image of a basic swing application.

import java.awt.EventQueue;
import javax.swing.JFrame;

public class GraalTest {

  public static void main( String ... args ) {
    EventQueue.invokeLater( () -> {
      JFrame frame = new JFrame( "test" );
      frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      frame.setVisible( true );
    } );
  }

}

Building it with

$GRAALVM_HOME/bin/javac GraalTest.java
$GRAALVM_HOME/bin/native-image GraalTest

The result is

Build on Server(pid: 21920, port: 37905)
[graaltest:21920]    classlist:     460,37 ms
[graaltest:21920]        (cap):     617,78 ms
[graaltest:21920]        setup:     933,59 ms
[graaltest:21920]     analysis:   9 108,10 ms
error: unsupported features in 4 methods
Detailed message:
Error: Detected a FileDescriptor in the image heap. File descriptors opened during image generation are no longer open at image run time, and the files might not even be present anymore at image run time. The object was probably created by a class initializer and is reachable from a static field. By default, all class initialization is done during native image building.You can manually delay class initialization to image run time by using the option --delay-class-initialization-to-runtime=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
Trace:  object java.io.RandomAccessFile
    object sun.nio.ch.FileChannelImpl
    object sun.font.TrueTypeFont$TTDisposerRecord
    object java.util.Hashtable$Entry
    object java.util.Hashtable$Entry
    object java.util.Hashtable$Entry[]
    object java.util.Hashtable
    method sun.java2d.Disposer.add(Object, DisposerRecord)
Call path from entry point to sun.java2d.Disposer.add(Object, DisposerRecord): 
    at sun.java2d.Disposer.add(Disposer.java:133)
    at sun.java2d.Disposer.addRecord(Disposer.java:121)
    at java.awt.Window.init(Window.java:517)
    at java.awt.Window.<init>(Window.java:537)
    at java.awt.Frame.<init>(Frame.java:420)
    at javax.swing.JFrame.<init>(JFrame.java:233)
    at GraalTest.lambda$main$0(GraalTest.java:8)
    at GraalTest$$Lambda$553/2015626550.run(Unknown Source)
    at java.lang.Shutdown.runHooks(Shutdown.java:123)
    at java.lang.Shutdown.sequence(Shutdown.java:167)
    at java.lang.Shutdown.shutdown(Shutdown.java:234)
    at com.oracle.svm.core.jdk.RuntimeSupport.shutdown(RuntimeSupport.java:179)
    at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:189)
    at com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)
Error: Detected a FileDescriptor in the image heap. File descriptors opened during image generation are no longer open at image run time, and the files might not even be present anymore at image run time. The object was probably created by a class initializer and is reachable from a static field. By default, all class initialization is done during native image building.You can manually delay class initialization to image run time by using the option --delay-class-initialization-to-runtime=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
Trace:  object sun.nio.ch.FileChannelImpl
    object sun.font.TrueTypeFont$TTDisposerRecord
    object java.util.Hashtable$Entry
    object java.util.Hashtable$Entry
    object java.util.Hashtable$Entry[]
    object java.util.Hashtable
    method sun.java2d.Disposer.add(Object, DisposerRecord)
Call path from entry point to sun.java2d.Disposer.add(Object, DisposerRecord): 
    at sun.java2d.Disposer.add(Disposer.java:133)
    at sun.java2d.Disposer.addRecord(Disposer.java:121)
    at java.awt.Window.init(Window.java:517)
    at java.awt.Window.<init>(Window.java:537)
    at java.awt.Frame.<init>(Frame.java:420)
    at javax.swing.JFrame.<init>(JFrame.java:233)
    at GraalTest.lambda$main$0(GraalTest.java:8)
    at GraalTest$$Lambda$553/2015626550.run(Unknown Source)
    at java.lang.Shutdown.runHooks(Shutdown.java:123)
    at java.lang.Shutdown.sequence(Shutdown.java:167)
    at java.lang.Shutdown.shutdown(Shutdown.java:234)
    at com.oracle.svm.core.jdk.RuntimeSupport.shutdown(RuntimeSupport.java:179)
    at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:189)
    at com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)
Error: Detected a started Thread in the image heap. Threads running in the image generator are no longer running at image run time. The object was probably created by a class initializer and is reachable from a static field. By default, all class initialization is done during native image building.You can manually delay class initialization to image run time by using the option --delay-class-initialization-to-runtime=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
Trace:  field sun.awt.X11.XToolkit.toolkitThread
Error: Detected a started Thread in the image heap. Threads running in the image generator are no longer running at image run time. The object was probably created by a class initializer and is reachable from a static field. By default, all class initialization is done during native image building.You can manually delay class initialization to image run time by using the option --delay-class-initialization-to-runtime=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
Trace:  object sun.java2d.opengl.OGLRenderQueue
    field sun.java2d.opengl.OGLRenderQueue.theInstance

Error: Processing image build request failed

I tried to understand how the class initialization works in the native image generation and that native resources and threads should not be allocated in static fields/initializers. But I'm still a bit puzzled here what am I doing wrong. As I'm trying to understand the first segment of the error message, I see that a TTF file is probably loaded in a static class initialization context while the window is created. But how can I avoid that?

cstancu commented 5 years ago

You can find more information about class initialization in this blog article.

horvathandras commented 5 years ago

I found the linked article while I was trying to understand what I was doing wrong, but it did not enlighten me. As far as I understand in the very moment when I try to create a JFrame, I indirectly import a class which opens a file in a static context (reading a TTF file for the menu of the window I guess) and interacts with a thread (the EDT I guess) which actions are not allowed on the image heap as the result of them will not be present in the runtime environment. So should I delay the initialization of the said class to be done at runtime with the --delay-class-initialization-to-runtime option? If yes, then which class should be delayed?

romixch commented 4 years ago

With GraalVM 19 the default behavior of class initialization has now flipped. But I still can't create any Native Image of a Swing application. I tested the code above with 19.2.1. But with no luck. It would be really cool to have a native image for my small swing app. The memory footprint of many hip desktop apps are crazy. I don't want to waist more gigabytes with my one.