简体   繁体   English

在Swing应用程序启动期间,首次调用JFrame构造函数需要很长时间(因为java.awt.Window())

[英]First call to JFrame constructor takes a long time during Swing application startup (because of java.awt.Window())

I'm trying to build a simple, lightweight, and responsive application using Java Swing. 我正在尝试使用Java Swing构建一个简单,轻量级且响应迅速的应用程序。 When it launches, however, there is a noticeable delay (>500ms) before the window (a JFrame) appears. 但是,当它启动时,在窗口(JFrame)出现之前会有明显的延迟(> 500ms)。

I've tracked it down to the constructor of the java.awt.Window class, which is an ancestor of JFrame. 我已经将它跟踪到java.awt.Window类的构造函数,该类是JFrame的祖先。

Oddly, the constructor is only slow for the first call. 奇怪的是,构造函数只对第一次调用很慢。 If I create multiple JFrame objects, the time spent in the constructor is ~600ms for the first object, but is typically measured as 0ms for subsequent objects. 如果我创建了多个JFrame对象,则构造函数在第一个对象上花费的时间约为600毫秒,但对于后续对象,通常测量为0毫秒。

Here's a simple example which, on my system, shows this significant delay for the first constructor call but not the second: 这是一个简单的例子,在我的系统中,它显示了第一个构造函数调用的显着延迟,但不是第二个:

public static void main(String args[]) {
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            long start;

            start = System.currentTimeMillis();
            JFrame frame1 = new JFrame();
            System.out.println((System.currentTimeMillis() - start) + " for first JFrame.");

            start = System.currentTimeMillis();
            JFrame frame2 = new JFrame();
            System.out.println((System.currentTimeMillis() - start) + " for second JFrame.");
        }
    });
}

With typical output: 具有典型输出:

641 for first JFrame.
0 for second JFrame.

If I add this Window object initialization before the JFrame objects: 如果我在JFrame对象之前添加此Window对象初始化:

java.awt.Window window = new java.awt.Window(null);

Then the output changes to something like: 然后输出变为如下:

578 for first Window.
47 for first JFrame.
0 for second JFrame.

When I try the same with the superclass of Window, java.awt.Container, the Window constructor is still the one that takes a long time to execute (so the problem doesn't go above the Window class). 当我尝试使用Window,java.awt.Container的超类时,Window构造函数仍然需要很长时间才能执行(因此问题不会超出Window类)。

Since the JFrame constructor calls the Window constructor, the above seems to indicate that the first call to the Window constructor is expensive. 由于JFrame构造函数调用Window构造函数,因此上面似乎表明对Window构造函数的第一次调用很昂贵。

What happens in the first call to the constructor that takes so long, and is there anything I can do about it? 在第一次调用构造函数时会发生什么,需要这么长时间,有什么我可以做的吗? Is there some simple fix or is the problem fundamental to Swing/AWT? 是否有一些简单的修复或者是Swing / AWT的基本问题? Or is it perhaps a problem specific to my system/setup? 或者它可能是我的系统/设置特有的问题?

I'd like my application to open as fast (or nearly as fast) as MS Notepad, and, while I can have text printed to the console around the time Notepad opens (if I put the code before the first JFrame initialization), the above problem means that there is almost a whole second of delay before the window is visible. 我希望我的应用程序能够像MS Notepad一样快速(或几乎同样快)打开,而且,虽然我可以在记事本打开时将文本打印到控制台(如果我在第一次JFrame初始化之前放置代码),上述问题意味着在窗口可见之前几乎有一整秒的延迟。 Will I need to use a different language or GUI framework to get the performance I'm after? 我是否需要使用不同的语言或GUI框架来获得我追求的性能?


Edit : If I add Thread.sleep(10000) as the first line of run(), the results don't change (they just appear 10 seconds later). 编辑 :如果我添加Thread.sleep(10000)作为run()的第一行,结果不会改变(它们只是在10秒后出现)。 This suggests that the problem is not caused by some asynchronous startup code but is instead triggered directly by the constructor call. 这表明问题不是由某些异步启动代码引起的,而是由构造函数调用直接触发。

Edit 2 : Realized the NetBeans Profiler can profile inside the JRE classes, and discovered that most of the time is spent initializing a sun.java2d.d3d.D3DGraphicsDevice object (the Window object needs screen bounds and insets), which is part of a "Direct3D Accelerated Rendering Pipeline for Microsoft Windows Platforms, Enabled by Default", introduced in Java 6u10 . 编辑2 :实现了NetBeans Profiler可以在JRE类中进行概要分析,并发现大部分时间都花在初始化sun.java2d.d3d.D3DGraphicsDevice对象上(Window对象需要屏幕边界和插入),这是“用于Microsoft Windows平台的Direct3D加速渲染管道,默认启用“,在Java 6u10中引入。 It can be disabled by passing the "-Dsun.java2d.d3d=false" property to the JVM, which does reduce the startup time by about 3/4, but I'm not yet sure if I'll need it (D3D) or if there's some other way to get it to load faster. 可以通过将“-Dsun.java2d.d3d = false”属性传递给JVM来禁用它,这会将启动时间减少大约3/4,但我还不确定是否需要它(D3D)或者,如果有其他方法可以让它加载更快。 Here is the output if I put that parameter on the command line: 如果我将该参数放在命令行上,则输出如下:

0 for first Window
47 for first JFrame.
0 for second JFrame.

I'll come back and clean this post up after I dig deeper later. 我稍后深入挖掘后,我会回来清理这篇文章。

This answer records what I have found so far. 这个答案记录了我到目前为止所发现的内容。 If anyone has more information, please comment or post an answer. 如果有人有更多信息,请评论或发布答案。 I am not completely satisfied with simply disabling Swing's use of D3D, and am open to other solutions. 我对完全禁用Swing使用D3D并不完全满意,并对其他解决方案持开放态度。

The Cause: D3D Initialization 原因:D3D初始化

Swing uses the Java2D API for drawing, and according to this Java SE 7 troubleshooting guide , Java2D uses a set of rendering pipelines, "which can be roughly defined as different ways of rendering the primitives". Swing使用Java2D API进行绘制,根据Java SE 7故障排除指南 ,Java2D使用一组渲染管道,“可以大致定义为渲染基元的不同方式”。 More specifically, a Java2D rendering pipeline seems to connect cross-platform Java code to native graphics libraries (OpenGL, X11, D3D, DirectDraw, GDI) that may support hardware acceleration. 更具体地说,Java2D渲染管道似乎将跨平台Java代码连接到可支持硬件加速的本机图形库(OpenGL,X11,D3D,DirectDraw,GDI)。

In Java 1.6.0_10 (aka 6u10) , a "fully hardware accelerated graphics pipeline" based on Direct3D was added to Java2D for Windows to improve rendering performance in Swing and Java2D applications (enabled by default). Java 1.6.0_10(又名6u10)中 ,基于Direct3D的“全硬件加速图形管道”被添加到Java2D for Windows中,以提高Swing和Java2D应用程序中的渲染性能(默认情况下启用)。

By default, when Java2D is used on a Windows system, both this Direct3D pipeline and a DirectDraw/GDI pipeline are enabled by default (I assume they are each being used for different things). 默认情况下,当在Windows系统上使用Java2D时,默认情况下启用此Direct3D管道和DirectDraw / GDI管道(我假设它们各自用于不同的事物)。

The D3D library, at least, is only loaded and initialized when needed, and a native D3D initialization function which gets called the first time a Window (or a Window descendant) is constructed takes ~500ms (for me) and causes the reported slowness of initialization, and disabling the D3D pipeline seems to remove the call to this native function, significantly decreasing startup time. 至少D3D库只在需要时加载和初始化,并且在第一次构建Window(或Window后代)时调用的本机D3D初始化函数需要~500ms(对我而言)并导致报告的缓慢初始化和禁用D3D管道似乎删除了对此本机函数的调用,大大减少了启动时间。 (Although I'd much prefer to delay, precompute, share (across different java apps), or optimize the D3D initialization, and I wonder if it's this slow for other languages.) (虽然我更喜欢延迟,预先计算,共享(跨不同的Java应用程序),或优化D3D初始化,我想知道其他语言是否会这么慢。)

Granted, it's possible that on most systems the time spent in D3D init is negligible, and it is only a problem on my system due to some hardware or driver problem, but I'm somewhat skeptical of this (although, if true, that would be an easy fix). 当然,在大多数系统上,D3D初始化所花费的时间可以忽略不计,而且由于某些硬件或驱动程序问题,这只是我系统的一个问题,但我对此持怀疑态度(尽管如果是真的,那就是是一个容易修复)。


Detailing the trace down to the native initD3D() 将跟踪细化到本机initD3D()

In more detail (skip the following paragraph if you don't care), I used the Netbeans profiler and debugger to find that: 更详细(如果你不在乎,请跳过以下段落),我使用Netbeans探查器和调试器来查找:

When a JFrame is initialized (constructor called), the constructor of ancestor class java.awt.Window gets called. 初始化JFrame(调用构造函数)时,将调用祖先类java.awt.Window的构造函数。 The Window initializes its GraphicsConfiguration device, which tries to retrieve the default screen device, and so on. Window初始化其GraphicsConfiguration设备,该设备尝试检索默认屏幕设备,依此类推。 The first time this happens (when the first Window or Window descendant is initialized), the screen device doesn't exist, so it is built. 第一次发生这种情况时(初始化第一个Window或Window后代),屏幕设备不存在,因此它是构建的。 In this process, the sun.java2D.d3d.D3DGraphicsDevice class gets initialized, and in its static initialization block (see <clinit>() ) it calls a native function, initD3D(), which takes a significant time to execute (~500ms). 在这个过程中,sun.java2D.d3d.D3DGraphicsDevice类被初始化,并在其静态初始化块(参见<clinit>() )中调用一个本机函数initD3D(),这需要很长的时间来执行(~500ms) )。

I was able to find a version of the source code for D3DGraphicsDevice and its static init block (and I'm really just assuming from this source that initD3D() is what makes its <clinit>() take so long - my profiler doesn't seem to acknowledge native functions - but it's a reasonable guess). 我能够找到D3DGraphicsDevice及其静态init块的源代码版本(我真的只是假设从这个源开始,initD3D()是什么让它的<clinit>()花了这么长时间 - 我的探查器没有'似乎承认原生功能 - 但这是一个合理的猜测)。


One workaround - disable D3D for Java2D 一种解决方法 - 为Java2D禁用D3D

The D3D pipeline can be disabled by running java with the -Dsun.java2d.d3d=false option, as per this guide on Java2D "system properties" (and also the aforementioned troubleshooting guide ). 根据Java2D“系统属性” (以及上述故障排除指南 )中的本指南,可以通过使用-Dsun.java2d.d3d=false选项运行java来禁用D3D管道。 I think this disables D3D but not DirectDraw, which can be disabled with Dsun.java2d.noddraw=true (and then "all operations will be performed with GDI"), but this doesn't noticeably improve initialization time. 我认为这会禁用D3D但不能禁用DirectDraw,可以使用Dsun.java2d.noddraw=true禁用它(然后“所有操作都将使用GDI执行”),但这并没有显着改善初始化时间。

For example, I might use a command like the following to run MyJar.jar without D3D: 例如,我可能会使用如下命令来运行没有D3D的MyJar.jar:

java -jar -Dsun.java2d.d3d=false MyJar.jar

With the code posted in the question (which initializes a Window and then 2 JFrame objects), I get results like this: 使用问题中的代码(初始化一个Window,然后是两个JFrame对象),我得到如下结果:

0 for first Window
47 for first JFrame.
0 for second JFrame.

Instead of results like this: 而不是像这样的结果:

547 for first Window
31 for first JFrame.
0 for second JFrame.

(Note that time is in milliseconds and is measured with System.currentTimeMillis() on Windows, which I think has a resolution of around 15 to 16 ms.) (请注意,时间以毫秒为单位,在Windows上使用System.currentTimeMillis()进行测量,我认为其分辨率大约为15到16毫秒。)


OpenGL vs Direct3D OpenGL与Direct3D

OpenGL is used instead of Direct3D if the -Dsun.java2d.opengl=True option is used. 如果使用-Dsun.java2d.opengl=True选项,则使用OpenGL代替Direct3D。 On my system there is a slight improvement (~400ms for OpenGL vs ~500ms for D3D), but the delay is still noticeable. 在我的系统上略有改进(OpenGL约为400ms,D3D为约500ms),但延迟仍然很明显。


Other Delays 其他延误

I noticed that initialization of the first JFrame object, even if it is not the first Window, takes much more time than the initialization of subsequent JFrame objects (recorded as 31 to 47 ms vs 0ms). 我注意到第一个JFrame对象的初始化,即使它不是第一个Window,也比后续JFrame对象的初始化花费更多的时间(记录为31到47毫秒对比0毫秒)。

Profiling indicates this is related to the creation of the first glass pane (a JPanel), and ultimately seems to be caused by Look and Feel and system property intialization/loading inside the javax.swing.UIManager class and object initialization code. 分析表明这与第一个玻璃窗格(JPanel)的创建有关,并且最终似乎是由javax.swing.UIManager类和对象初始化代码中的外观和系统属性初始化/加载引起的。 It's not too important, but it does explain the observed anomaly. 它并不太重要,但确实解释了观察到的异常现象。

In my actual program which is a bit more complicated (has to initialize more Swing components), delays seem to be more diffusely distributed in the Swing code, but I've noticed a significant amount of native class loading, "UI installing" (loading default UI properties, etc.), and such. 在我的实际程序中有点复杂(必须初始化更多的Swing组件),延迟似乎在Swing代码中更加分散,但我注意到了大量的本机类加载,“UI安装”(加载默认UI属性等),等等。 Unfortunately, I don't think there's much to be done about these (please speak up if there is). 不幸的是,我认为这些问题还有很多工作要做(如果有,请说出来)。


Closing thoughts 结束思想

In the end, there's only so much that can be done, and I have to recognize how far Swing and the JVM have come in the past years. 最后,只有这么多可以完成的事情,我必须认识到Swing和JVM在过去几年中已经走了多远。

The creation of the first jframe involves loading the swing library. 第一个jframe的创建涉及加载swing库。 JVM doesn't load the library looking at the import statement. JVM不会加载库查看import语句。 The libraries are loaded only when the first call to that library is made. 仅在第一次调用该库时才加载库。

In this case frame1 creation is the first statement that calls the Swing library. 在这种情况下,frame1创建是第一个调用Swing库的语句。 By the time frame2 instance is created the swing libraries are already loaded and hence the object creation for frame2 is too fast even to notice some lapse of time. 在创建frame2实例时,已经加载了swing库,因此frame2的对象创建速度太快,甚至没有注意到一些时间的流逝。 Hence it shows 0. 因此它显示0。

This explains why it shows 578, 47, 0 when you add up Window statement above the two. 这解释了为什么当你将Window语句加在两者之上时它显示578,47,0。 This is because the first statement takes time to load java.awt library. 这是因为第一个语句需要时间来加载java.awt库。 Second takes time to load the swing library. 第二个需要时间来加载swing库。 And the thirds shows 0 as the library needed for its creation is already loaded. 并且三分之一显示为0,因为其创建所需的库已经加载。

You can even test it this way. 你甚至可以用这种方式测试它。 Try to replace the second JFrame creation with JPanel and it still it shows 0. 尝试用JPanel替换第二个JFrame创建,它仍然显示0。

My guess is that it's loading native libraries. 我的猜测是它正在加载本机库。 If this is the case, there is not much you can do about it. 如果是这种情况,那么你无能为力。

Mac OS X 10.5.8, Java 1.6.0_26. Mac OS X 10.5.8,Java 1.6.0_26。 Not unexpected, given the JVM startup time and heavyweight graphics peer creation. 考虑到JVM启动时间和重量级图形对等创建,这并不意外。

247 for first JFrame.
0 for second JFrame.

Addendum: According to the article Java Performance: Startup time , Java Quick Starter may be available on your platform. 附录:根据文章Java性能:启动时间 ,您可以在您的平台上使用Java Quick Starter

It takes time to load all swing classes, then it takes time to load native awt libraries. 加载所有swing类需要时间,然后加载本机awt库需要时间。 Perhaps, class loading takes more time because if you just create a JLabel instead of the first JFrame it still takes more time. 也许,类加载需要更多时间,因为如果你只是创建一个JLabel而不是第一个JFrame,它仍然需要更多时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM