WPF 应用程序启动所需的时间可能会很大。 本主题介绍用于减少 Windows Presentation Foundation (WPF) 应用程序的感知和实际启动时间的各种技术。
了解冷启动和暖启动
当应用程序在系统重新启动后首次启动或启动应用程序时,关闭它,然后在长时间后再次启动它时,就会发生冷启动。 应用程序启动时,如果 Windows 内存管理器的备用列表中不存在所需的页面(代码、静态数据、注册表等),则会发生页面错误。 磁盘访问是将页面引入内存所必需的。
当主要公共语言运行时 (CLR) 组件的大多数页面已加载到内存中时,将发生热启动,从而节省昂贵的磁盘访问时间。 这就是为什么托管应用程序第二次运行时启动速度更快的原因。
启动画面的实现
如果启动应用程序与显示第一个 UI 之间存在重大、不可避免的延迟,请使用 初始屏幕优化感知的启动时间。 此方法在用户启动应用程序后几乎立即显示图像。 当应用程序准备好显示其第一个 UI 时,启动画面将淡出。 从 .NET Framework 3.5 SP1 开始,可以使用 SplashScreen 该类实现初始屏幕。 有关详细信息,请参阅 向 WPF 应用程序添加初始屏幕。
还可以使用本机 Win32 图形实现自己的初始屏幕。 在调用 Run 方法之前,显示您的实现。
分析启动代码
确定缓慢冷启动的原因。 磁盘 I/O 可能负责,但这并非总是如此。 通常,应最大程度地减少外部资源的使用,例如网络、Web 服务或磁盘。
在测试之前,请验证没有其他正在运行的应用程序或服务使用托管代码或 WPF 代码。
重新启动后立即启动 WPF 应用程序,并确定显示需要多长时间。 如果应用程序(热启动)的所有后续启动速度要快得多,则冷启动问题很可能是由 I/O 引起的。
如果应用程序的冷启动问题与 I/O 无关,则应用程序执行一些冗长的初始化或计算、等待某些事件完成或在启动时需要大量 JIT 编译。 以下部分更详细地介绍了其中一些情况。
优化模块加载
使用进程资源管理器(Procexp.exe)和 Tlist.exe 等工具来确定应用程序加载的模块。 该命令 Tlist <pid>
显示进程加载的所有模块。
例如,如果未连接到 Web,并且看到已加载 System.Web.dll,则应用程序中有一个引用此程序集的模块。 检查以确保引用是必需的。
如果应用程序有多个模块,请将它们合并到单个模块中。 此方法需要更少的 CLR 程序集加载开销。 更少的程序集也意味着 CLR 保持较少的状态。
延迟初始化操作
请考虑推迟初始化代码,直到呈现主应用程序窗口之后。
请注意,初始化可能在类构造函数内执行,如果初始化代码引用其他类,则可能会导致执行许多类构造函数的级联效果。
避免应用程序配置
请考虑避免应用程序配置。 例如,如果应用程序具有简单的配置要求,并且具有严格的启动时间目标,则注册表项或简单的 INI 文件可能是更快的启动替代方法。
利用 GAC
如果未在全局程序集缓存中安装程序集(GAC),则强名称程序集的哈希验证和 Ngen 映像验证(如果计算机上的程序集的本机映像可用)会导致延迟。 对于 GAC 中安装的所有程序集,将跳过强名称验证。 有关详细信息,请参阅 Gacutil.exe(全局程序集缓存工具)。
使用 Ngen.exe
请考虑在应用程序中使用本机映像生成器(Ngen.exe)。 使用 Ngen.exe 意味着将 CPU 资源用于更多的磁盘访问,因为由 Ngen.exe 生成的本机映像可能大于 MSIL 映像。
为了缩短热启动时间,应始终在应用程序中使用 Ngen.exe,因为这可避免 JIT 编译应用程序代码的 CPU 成本。
在某些冷启动场景中,使用 Ngen.exe 也很有帮助。 这是因为 JIT 编译器(mscorjit.dll)不必加载。
拥有 Ngen 和 JIT 模块可能会产生最差的影响。 这是因为必须加载 mscorjit.dll,当 JIT 编译器在代码上运行时,当 JIT 编译器读取程序集的元数据时,必须访问 Ngen 映像中的许多页面。
Ngen 和 ClickOnce
计划部署应用程序的方式也可能会改变加载时间。 ClickOnce 应用程序部署不支持 Ngen。 如果决定对应用程序使用 Ngen.exe,则必须使用另一种部署机制,例如 Windows Installer。
有关详细信息,请参阅 Ngen.exe(本机映像生成器)。
Rebasing 和 DLL 地址冲突
如果使用 Ngen.exe,请注意,当本机映像加载到内存中时,可能会发生重定位。 如果 DLL 未在其首选基址上加载,因为该地址范围已分配,则 Windows 加载程序将在另一个地址加载它,这可能会是一个耗时的操作。
可以使用虚拟地址转储(Vadump.exe)工具来检查是否存在所有页面都是专用的模块。 如果是这种情况,则模块可能已重新设置为其他地址。 因此,无法共享其页面。
有关如何设置基址的详细信息,请参阅 Ngen.exe(本机映像生成器)。
优化验证码
验证码验证会添加到启动时间。 验证码签名的程序集必须通过证书颁发机构(CA)进行验证。 此验证可能非常耗时,因为它可能需要多次连接到网络才能下载当前证书吊销列表。 它还确保受信任的根路径上有一个完整的有效证书链。 在加载程序集时,这可以转换为几秒钟的延迟。
请考虑在客户端计算机上安装 CA 证书,或者尽可能避免使用 Authenticode。 如果知道应用程序不需要发布者证据,则无需支付签名验证的费用。
从 .NET Framework 3.5 开始,有一个配置选项允许绕过验证码验证。 为此,请将以下设置添加到 app.exe.config 文件:
<configuration>
<runtime>
<generatePublisherEvidence enabled="false"/>
</runtime>
</configuration>
有关详细信息,请参阅 <generatePublisherEvidence> 元素。
比较 Windows Vista 上的性能
Windows Vista 中的内存管理器具有名为 SuperFetch 的技术。 SuperFetch 会分析一段时间内的内存使用模式,以确定特定用户的最佳内存内容。 它始终致力于维护该内容。
此方法不同于 Windows XP 中使用的预提取技术,该方法无需分析使用模式即可将数据预加载到内存中。 随着时间的推移,如果用户经常在 Windows Vista 上使用 WPF 应用程序,则应用程序的冷启动时间可能会提高。
高效使用 AppDomains
如果可能,将程序集加载到域中立代码区域中,以确保在应用程序中创建的所有应用程序域中使用本机映像(如果存在)。
为了获得最佳性能,通过减少跨域调用来强制实施高效的跨域通信。 如果可能,请使用不带参数的调用或基元类型参数。
使用 NeutralResourcesLanguage 属性
使用 NeutralResourcesLanguageAttribute 指定中性区域性 ResourceManager。 此方法可避免程序集查找失败。
使用 XML 序列化程序生成器
如果使用 XmlSerializer 类,如果使用 XML 序列化程序生成器工具(Sgen.exe)预生成序列化程序集,则可以获得更好的性能。
配置 ClickOnce 以在启动后检查更新
如果应用程序使用 ClickOnce,请在启动时通过配置 ClickOnce 来检查部署站点,以便在应用程序启动后检查更新,从而避免网络访问。
如果使用 XAML 浏览器应用程序 (XBAP) 模型,请记住,即使 XBAP 已在 ClickOnce 缓存中,ClickOnce 也会检查部署站点是否有更新。 有关详细信息,请参阅 ClickOnce 安全性和部署。
警告
XBAP 要求旧版浏览器运行,例如 Internet Explorer 和旧版 Firefox。 这些较旧的浏览器通常在 Windows 10 和 Windows 11 上不受支持。 由于安全风险,新式浏览器不再支持 XBAP 应用所需的技术。 不再支持启用 XBAP 的插件。 有关详细信息,请参阅 有关 WPF 浏览器托管应用程序(XBAP)的常见问题解答。
将 PresentationFontCache 服务配置为自动启动
重新启动后运行的第一个 WPF 应用程序是 PresentationFontCache 服务。 服务会缓存系统字体、改进字体访问权限,并提高整体性能。 启动服务会产生开销,在某些受控环境中,请考虑将服务配置为在系统重新启动时自动启动。
以编程方式设置数据绑定
请考虑在OnActivated方法中以编程方式设置主窗口,而不是使用 XAML 以声明方式设置DataContext。