A C*_*der 13 java optimization swing
我正在尝试使用Java Swing构建一个简单,轻量级且响应迅速的应用程序.但是,当它启动时,在窗口(JFrame)出现之前会有明显的延迟(> 500ms).
我已经将它跟踪到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.");
}
});
}
Run Code Online (Sandbox Code Playgroud)
具有典型输出:
641 for first JFrame.
0 for second JFrame.
Run Code Online (Sandbox Code Playgroud)
如果我在JFrame对象之前添加此Window对象初始化:
java.awt.Window window = new java.awt.Window(null);
Run Code Online (Sandbox Code Playgroud)
然后输出变为如下:
578 for first Window.
47 for first JFrame.
0 for second JFrame.
Run Code Online (Sandbox Code Playgroud)
当我试着使用窗口的超类相同,java.awt.Container中,窗口的构造函数仍是一个需要很长的时间来执行(所以这个问题不走Window类以上).
由于JFrame构造函数调用Window构造函数,因此上面似乎表明对Window构造函数的第一次调用很昂贵.
在第一次调用构造函数时会发生什么,需要这么长时间,有什么我可以做的吗?是否有一些简单的修复或者是Swing/AWT的基本问题?或者它可能是我的系统/设置特有的问题?
我想我的应用程序打开的速度(或几乎一样快)为MS记事本,而我可以打印到身边的时候,记事本的控制台文本打开(如果我第一JFrame的初始化之前把代码),该上述问题意味着在窗口可见之前几乎有一整秒的延迟.我是否需要使用不同的语言或GUI框架来获得我追求的性能?
编辑:如果我添加Thread.sleep(10000)作为run()的第一行,结果不会改变(它们只是在10秒后出现).这表明问题不是由某些异步启动代码引起的,而是由构造函数调用直接触发.
编辑2:实现了NetBeans Profiler可以在JRE类中进行概要分析,并发现大部分时间都花在初始化sun.java2d.d3d.D3DGraphicsDevice对象上(Window对象需要屏幕边界和插入),这是"用于Microsoft Windows平台的Direct3D加速渲染管道,默认启用",在Java 6u10中引入.可以通过将"-Dsun.java2d.d3d = false"属性传递给JVM来禁用它,这会将启动时间减少大约3/4,但我还不确定是否需要它(D3D)或者,如果有其他方法可以让它加载更快.如果我将该参数放在命令行上,则输出如下:
0 for first Window
47 for first JFrame.
0 for second JFrame.
Run Code Online (Sandbox Code Playgroud)
我稍后深入挖掘后,我会回来清理这篇文章.
这个答案记录了我到目前为止所发现的内容.如果有人有更多信息,请评论或发布答案.我对完全禁用Swing使用D3D并不完全满意,并对其他解决方案持开放态度.
原因:D3D初始化
Swing使用Java2D API进行绘制,根据Java SE 7故障排除指南,Java2D使用一组渲染管道,"可以大致定义为渲染基元的不同方式".更具体地说,Java2D渲染管道似乎将跨平台Java代码连接到可支持硬件加速的本机图形库(OpenGL,X11,D3D,DirectDraw,GDI).
在Java 1.6.0_10(又名6u10)中,基于Direct3D的"全硬件加速图形管道"被添加到Java2D for Windows中,以提高Swing和Java2D应用程序中的渲染性能(默认情况下启用).
默认情况下,当在Windows系统上使用Java2D时,默认情况下启用此Direct3D管道和DirectDraw/GDI管道(我假设它们各自用于不同的事物).
至少D3D库只在需要时加载和初始化,并且在第一次构建Window(或Window后代)时调用的本机D3D初始化函数需要~500ms(对我而言)并导致报告的缓慢初始化和禁用D3D管道似乎删除了对此本机函数的调用,大大减少了启动时间.(虽然我更喜欢延迟,预先计算,共享(跨不同的Java应用程序),或优化D3D初始化,我想知道其他语言是否会这么慢.)
当然,在大多数系统中,D3D初始化所花费的时间可以忽略不计,而且由于某些硬件或驱动程序问题,这只是我系统的一个问题,但我对此持怀疑态度(尽管如果是真的,那就是是一个容易修复).
将跟踪细化到本机initD3D()
更详细(如果你不在乎,请跳过以下段落),我使用Netbeans探查器和调试器来查找:
初始化JFrame(调用构造函数)时,将调用祖先类java.awt.Window的构造函数.Window初始化其GraphicsConfiguration设备,该设备尝试检索默认屏幕设备,依此类推.第一次发生这种情况时(初始化第一个Window或Window后代),屏幕设备不存在,因此它是构建的.在这个过程中,sun.java2D.d3d.D3DGraphicsDevice类被初始化,并在其静态初始化块(参见<clinit>())中调用一个本机函数initD3D(),这需要很长的时间来执行(~500ms) ).
我能够找到D3DGraphicsDevice及其静态init块的源代码版本(我真的只是假设从这个源开始,initD3D()是什么让它的<clinit>()花了这么长时间 - 我的探查器没有'似乎承认原生功能 - 但这是一个合理的猜测).
一种解决方法 - 为Java2D禁用D3D
根据Java2D"系统属性"(以及上述故障排除指南)中的-Dsun.java2d.d3d=false本指南,可以通过运行带有该选项的java来禁用D3D管道.我认为这会禁用D3D但不禁用DirectDraw,可以禁用它(然后"所有操作都将使用GDI执行"),但这并没有显着改善初始化时间.Dsun.java2d.noddraw=true
例如,我可能会使用如下命令来运行没有D3D的MyJar.jar:
java -jar -Dsun.java2d.d3d=false MyJar.jar
Run Code Online (Sandbox Code Playgroud)
使用问题中的代码(初始化一个Window,然后是两个JFrame对象),我得到如下结果:
0 for first Window
47 for first JFrame.
0 for second JFrame.
Run Code Online (Sandbox Code Playgroud)
而不是像这样的结果:
547 for first Window
31 for first JFrame.
0 for second JFrame.
Run Code Online (Sandbox Code Playgroud)
(请注意,时间以毫秒为单位,在Windows上使用System.currentTimeMillis()进行测量,我认为其分辨率大约为15到16毫秒.)
OpenGL与Direct3D
如果使用该-Dsun.java2d.opengl=True选项,则使用OpenGL代替Direct3D .在我的系统上略有改进(OpenGL约为400ms,D3D为约500ms),但延迟仍然很明显.
其他延误
我注意到第一个JFrame对象的初始化,即使它不是第一个Window,也比后续JFrame对象的初始化花费更多的时间(记录为31到47毫秒对比0毫秒).
分析表明这与第一个玻璃窗格(JPanel)的创建有关,并且最终似乎是由javax.swing.UIManager类和对象初始化代码中的外观和系统属性初始化/加载引起的.它并不太重要,但确实解释了观察到的异常现象.
在我的实际程序中有点复杂(必须初始化更多的Swing组件),延迟似乎在Swing代码中更加分散,但我注意到了大量的本机类加载,"UI安装"(加载默认UI属性等),等等.不幸的是,我认为这些问题还有很多工作要做(如果有,请说出来).
结束思想
最后,只有这么多可以完成的事情,我必须认识到Swing和JVM在过去几年中已经走了多远.