我正在尝试使用Java Swing构建一个简单,轻便且响应 swift 应用程序。
但是,启动时,在窗口(JFrame)出现之前会有明显的延迟(> 500ms)。

我已经将其追溯到java.awt.Window类的构造函数,该类是JFrame的祖先。

奇怪的是,构造函数仅在第一次调用时很慢。如果我创建多个JFrame对象,则第一个对象在构造函数中花费的时间约为600ms,但对于后续对象通常为0ms。

这是一个简单的示例,在我的系统上,它显示了第一个构造函数调用的显着延迟,而第二个则没有:

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.");
        }
    });
}

典型输出:
641 for first JFrame.
0 for second JFrame.

如果我在JFrame对象之前添加此Window对象初始化:
java.awt.Window window = new java.awt.Window(null);

然后输出更改为:
578 for first Window.
47 for first JFrame.
0 for second JFrame.

当我尝试使用Window的父类(super class)java.awt.Container进行相同操作时,Window构造函数仍然需要花费很长时间才能执行(因此问题不会超出Window类)。

由于JFrame构造函数调用Window构造函数,因此以上内容似乎表明对Window构造函数的第一次调用是昂贵的。

第一次调用构造函数需要很长时间,会发生什么情况,我对此有什么能做的吗?
是否有一些简单的解决方法或该问题是Swing/AWT的根本问题?也许是我的系统/设置特有的问题?

我希望我的应用程序能以与MS Notepad一样快(或几乎一样快)的速度打开,并且尽管我可以在打开Notepad的同时在控制台上打印文本(如果我将代码放在第一次JFrame初始化之前),上述问题意味着在窗口可见之前几乎有一整秒的延迟。我是否需要使用其他语言或GUI框架来获得所需的性能?

编辑:如果我将Thread.sleep(10000)添加为run()的第一行,结果不会改变(它们只会在10秒后出现)。这表明该问题不是由某些异步启动代码引起的,而是由构造函数调用直接触发的。

编辑2 :实现了NetBeans Profiler可以在JRE类中进行概要分析,并发现大部分时间都花在初始化sun.java2d.d3d.D3DGraphicsDevice对象上(Window对象需要屏幕边界和插图)。 Java 6u10中介绍了“默认情况下启用的Microsoft Windows平台的Direct3D加速渲染管道”。可以通过将“-Dsun.java2d.d3d = false”属性传递给JVM来禁用它,这确实可以将启动时间减少大约3/4,但是我不确定是否需要它(D3D)或者是否有其他方法可以使其更快地加载。
如果将该参数放在命令行上,则输出如下:
0 for first Window
47 for first JFrame.
0 for second JFrame.

稍后再深入研究后,我会回来整理这篇文章。

最佳答案

这个答案记录了我到目前为止所发现的。如果有人有更多信息,请发表评论或发表答案。我对仅禁用Swing对D3D的使用并不完全满意,并且愿意接受其他解决方案。

原因:D3D初始化

Swing使用Java2D API进行绘制,并且根据此Java SE 7 troubleshooting guide,Java2D使用了一组渲染管道,“它们可以粗略地定义为渲染图元的不同方式”。更具体地说,Java2D渲染管道似乎将跨平台的Java代码连接到可能支持硬件加速的 native 图形库(OpenGL,X11,D3D,DirectDraw,GDI)。

Java 1.6.0_10 (aka 6u10)中,基于Direct3D的“完全硬件加速的图形管道”已添加到Windows的Java2D中,以提高Swing和Java2D应用程序的渲染性能(默认情况下启用)。

默认情况下,在Windows系统上使用Java2D时,默认情况下会同时启用此Direct3D管道和DirectDraw/GDI管道(我假设它们分别用于不同的事物)。

至少D3D库仅在需要时才加载和初始化,并且 native D3D初始化函数在第一次构造Window(或Window子孙)时被调用,耗时约500ms(对我而言),并导致报告的缓慢初始化以及禁用D3D管道似乎都删除了对该本地函数的调用,从而大大减少了启动时间。 (尽管我更愿意延迟,预先计算,共享(在不同的Java应用程序之间)或优化D3D初始化,但我想知道其他语言是否这么慢)。

当然,在大多数系统上花费在D3D init上的时间可以忽略不计,并且由于某些硬件或驱动程序问题,这只是我系统上的一个问题,但是我对此有些怀疑(尽管如果确实如此,那轻松解决)。

详细跟踪到 native initD3D()

更详细地讲(如果您不在乎,请跳过以下段落),我使用Netbeans探查器和调试器进行了查找:

初始化JFrame(调用构造函数)后,将调用祖先类java.awt.Window的构造函数。 Window初始化其GraphicsConfiguration设备,该设备尝试检索默认的屏幕设备,依此类推。第一次发生(初始化第一个Window或Window子孙)时,屏幕设备不存在,因此将其构建。在此过程中,将初始化sun.java2D.d3d.D3DGraphicsDevice类,并在其静态初始化块(请参见<clinit>())中调用 native 函数initD3D(),这需要花费大量时间才能执行(约500毫秒)。

我能够找到source code for D3DGraphicsDevice及其静态init块的版本(而且我实际上只是从此来源假设initD3D()是使()花费这么长时间的原因-我的分析器似乎没有承认 native 函数-但这是一个合理的猜测)。

一种解决方法-禁用Java2D的D3D

可以根据此guide on Java2D "system properties"(以及上述的troubleshooting guide),通过使用-Dsun.java2d.d3d=false选项运行java来禁用D3D管道。我认为这会禁用D3D,但不会禁用DirectDraw,后者可以使用Dsun.java2d.noddraw=true禁用(然后“所有操作都将使用GDI执行”),但这并不能显着缩短初始化时间。

例如,我可能使用以下命令在没有D3D的情况下运行MyJar.jar:

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

使用问题中发布的代码(该代码初始化一个Window,然后初始化2个JFrame对象),我得到如下结果:
0 for first Window
47 for first JFrame.
0 for second JFrame.

而不是像这样的结果:
547 for first Window
31 for first JFrame.
0 for second JFrame.

(请注意,时间以毫秒为单位,并且使用Windows上的System.currentTimeMillis()进行测量,我认为其分辨率约为15到16毫秒。)

OpenGL与Direct3D

如果使用-Dsun.java2d.opengl=True选项,则使用OpenGL代替Direct3D。在我的系统上,有一点改进(OpenGL为〜400ms,D3D为〜500ms),但是延迟仍然很明显。

其他延迟

我注意到,第一个JFrame对象的初始化(即使它不是第一个Window)也比后续JFrame对象的初始化花费更多的时间(记录为31到47 ms vs 0ms)。

分析表明这与第一个玻璃 Pane (JPanel)的创建有关,并且最终似乎是由外观和感觉以及javax.swing.UIManager类和对象初始化代码中的系统属性初始化/加载引起的。它不是太重要,但是确实可以解释观察到的异常。

在我的实际程序中,它有点复杂(必须初始化更多的Swing组件),延迟似乎更分散地分布在Swing代码中,但是我注意到大量的本地类加载,即“UI安装”(加载默认的UI属性等)。不幸的是,我认为没有太多要做(请大声说)。

结束语

最后,只能做很多事情,而且我必须认识到过去几年Swing和JVM的发展。

10-08 18:15