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

标签 java swing optimization

我正在尝试使用 Java Swing 构建一个简单、轻量级且响应迅速的应用程序。
但是,当它启动时,在窗口(JFrame)出现之前会有明显的延迟(> 500 毫秒)。

我已经追踪到 java.awt.Window 类的构造函数,它是 JFrame 的祖先。

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

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

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 记事本一样快(或几乎一样快),而且,虽然我可以在记事本打开前后将文本打印到控制台(如果我将代码放在第一次 JFrame 初始化之前),上述问题意味着在窗口可见之前几乎有一整秒的延迟。我是否需要使用不同的语言或 GUI 框架来获得我想要的性能?

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

编辑 2 : 实现了 NetBeans Profiler 可以在 JRE 类内部进行分析,发现大部分时间都花在了初始化 sun.java2d.d3d.D3DGraphicsDevice 对象上(Window 对象需要 screen bounds 和 insets),这是“Direct3D Accelerated”的一部分Microsoft Windows 平台的渲染管道,默认启用”,在 Java 6u10 中引入.可以通过将“-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 的“全硬件加速图形管道”被添加到 Java2D for Windows 中,以提高 Swing 和 Java2D 应用程序的渲染性能(默认启用)。

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

至少 D3D 库仅在需要时才加载和初始化,并且在第一次构造 Window(或 Window 后代)时调用的 native D3D 初始化函数需要约 500 毫秒(对我而言)并导致报告的缓慢初始化和禁用 D3D 管道似乎删除了对这个 native 函数的调用,显着减少了启动时间。 (虽然我更喜欢延迟、预计算、共享(跨不同的 Java 应用程序)或优化 D3D 初始化,但我想知道其他语言是否会这么慢。)

诚然,在大多数系统上花费在 D3D init 上的时间可能可以忽略不计,这只是我的系统上由于某些硬件或驱动程序问题而导致的问题,但我对此有些怀疑(尽管,如果属实,那将是一个简单的修复)。

详细跟踪到 native initD3D()

更详细地(如果您不在乎,请跳过以下段落),我使用 Netbeans 分析器和调试器来发现:

当 JFrame 被初始化(构造函数被调用)时,祖先类 java.awt.Window 的构造函数被调用。 Window 初始化它的 GraphicsConfiguration 设备,它尝试检索默认屏幕设备,等等。第一次发生这种情况(当第一个 Window 或 Window 后代被初始化时),屏幕设备不存在,所以它被构建。在这个过程中, sun.java2D.d3d.D3DGraphicsDevice 类被初始化,并在它的静态初始化块(见 <clinit>() )中调用一个 native 函数 initD3D(),它需要很长的时间来执行(~500ms)。

我找到了 source code for D3DGraphicsDevice 的版本和它的静态 init 块(我真的只是从这个来源假设 initD3D() 是它的 () 花费这么长时间的原因 - 我的分析器似乎不承认 native 函数 - 但这是一个合理的猜测) .

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

可以通过使用 -Dsun.java2d.d3d=false 运行 java 来禁用 D3D 管道。选项,按照此 guide on Java2D "system properties" (还有前面提到的 troubleshooting guide )。我认为这会禁用 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 约 400 毫秒,D3D 约 500 毫秒),但延迟仍然很明显。

其他延误

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

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

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

结束语

最后,能做的只有这么多,我必须认识到 Swing 和 JVM 在过去几年里走了多远。

关于java - 在 Swing 应用程序启动期间,首次调用 JFrame 构造函数需要很长时间(因为 java.awt.Window()),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6978017/

相关文章:

java - JLabel 中的图标被隐藏

java - 在复杂环境中处理属性和 log4j 的工具

java - 使用等待和通知的死锁

Java图像转换为RGB565

java - 从 ComboBox 中进行选择时,用数据库条目填充 JList

python : Solving a problem of finding a combination which satisfies a particular condition

java - 大对象不能在自动提交模式下使用

java - 滚动时缓慢更改背景

Java:为变量分配当前值?

optimization - 为什么我的移动平均算法这么慢?