アプリ起動時のパフォーマンスのベスト プラクティス
起動とアクティブ化を処理する方法を向上させることによって、最適な起動時間のユニバーサル Windows プラットフォーム (UWP) アプリを作成します。
アプリ起動時のパフォーマンスのベスト プラクティス
ユーザーがアプリを速いまたは遅いと感じる要因の 1 つとして、アプリの起動にかかる時間があります。 このトピックの目的上、アプリの起動時間は、ユーザーがアプリを開始したときに始まり、何らかの意味のある方法でアプリを操作できるようになったときに終わるものとします。 このセクションでは、アプリ起動時のパフォーマンスを向上させるための推奨事項を紹介します。
アプリの起動時間の測定
アプリを数回起動してから、実際に起動時間を測定するようにしてください。 そうすることで測定のベースラインが確立され、合理的に可能な限り短い起動時間を測定できるようになります。
UWP アプリが顧客のコンピューターに届くまでに、アプリは .NET ネイティブ ツール チェーンを使ってコンパイルされます。 .NET ネイティブは、MSIL をネイティブに実行可能なマシン コードに変換する事前コンパイル テクノロジです。 .NET ネイティブ アプリは、MSIL アプリに比べて、すばやく起動し、メモリ使用量やバッテリ使用量が少なくて済みます。 .NET ネイティブを使ってビルドされたアプリケーションはカスタム ランタイムおよびあらゆるデバイスで実行できる新しい集約型の .NET Core に静的にリンクされるため、インボックスの .NET の実装に依存しません。 開発コンピューターで、アプリを "リリース" モードでビルドしている場合、アプリは既定で .NET ネイティブを使用し、アプリを "デバッグ" モードでビルドしている場合は CoreCLR を使用します。 Visual Studio では、[プロパティ] の [ビルド] ページ (C# の場合) または [マイ プロジェクト] の [コンパイル] -> [詳細設定] (VB の場合) でこれを構成できます。 [.NET ネイティブ ツール チェーンでコンパイルする] というチェック ボックスを探します。
当然、エンド ユーザーが経験する起動時間に相当する時間を測定する必要があります。 したがって、開発コンピューターでアプリをネイティブ コードにコンパイルしているかどうかわからない場合は、アプリの起動時間を測定する前に、ネイティブ イメージ ジェネレーター (Ngen.exe) ツールを実行してアプリをプリコンパイルすることができます。
次の手順では、Ngen.exe を実行してアプリをプリコンパイルする方法について説明します。
Ngen.exe を実行するには
Ngen.exe がアプリを検出することを確認するために、アプリを少なくとも 1 回実行します。
次のいずれかの方法でタスク スケジューラを開きます。
- スタート画面で「タスク スケジューラ」を検索します。
- taskschd.msc を実行します。
タスク スケジューラの左ペインで [タスク スケジューラ ライブラリ] を展開します。
[Microsoft] を展開します。
[Windows] を展開します。
[.NET Framework] を選択します。
タスクの一覧から [.NET Framework NGEN 4.x] を選択します。
64 ビット コンピューターを使っている場合は、[.NET Framework NGEN v4.x 64] も表示されます。 64 ビット アプリを開発している場合は、[.NET Framework NGEN v4.x 64] を選択します。
[操作] メニューの [実行] をクリックします。
Ngen.exe は、使用されたことがあり、ネイティブ イメージを持たない、コンピューター上のすべてのアプリをプリコンパイルします。 プリコンパイルする必要があるアプリが多数あるとプリコンパイルに時間がかかりますが、その後の実行時間は大幅に短縮されます。
アプリを再コンパイルすると、ネイティブ イメージは使われなくなります。 一方、アプリをジャスト イン タイムでコンパイルする場合、アプリは実行時にコンパイルされます。 新しいネイティブ イメージを取得するには Ngen.exe を再実行する必要があります。
可能な限りの処理の遅延
アプリの起動時間を短縮するには、ユーザーがアプリの操作を開始するために絶対に必要な処理だけを実行します。 この手法が特に効果的なのは、追加のアセンブリの読み込みを遅延させることができる場合です。 共通言語ランタイムは、初めて使用するときにアセンブリを読み込みます。 読み込まれるアセンブリの数を最小限に抑えることができれば、アプリの起動時間とメモリ消費量を削減できる可能性があります。
長時間の処理の個別実行
完全に機能していない部分がアプリにあっても、アプリはデータをやり取りすることができます。 たとえば、取得に時間がかかるデータをアプリが表示する場合は、データを非同期的に取得することによって、アプリの起動コードとは別にそのコードを実行することができます。 データが利用可能になると、アプリのユーザー インターフェイスにデータが入力されます。
データを取得するユニバーサル Windows プラットフォーム (UWP) API の多くは非同期であるため、いずれにしてもデータはほぼ確実に非同期的に取得されます。 非同期 API について詳しくは、「C# または Visual Basic での非同期 API の呼び出し」をご覧ください。 非同期 API を使用しない作業を行う場合は、Task クラスを使って長時間実行される作業を行うことで、ユーザーによるアプリの操作がブロックされないようにすることができます。 そうすることで、データの読み込み中もユーザーに対するアプリの応答性が維持されます。
アプリが UI の一部を読み込むのに特に長い時間がかかる場合は、UI のその部分に「最新のデータを取得中」などの文字列を表示して、アプリが処理中であることをユーザーに知らせることを検討してください。
起動時間の最小化
非常にシンプルなアプリを除くほとんどのアプリでは、リソースの読み込み、XAML の解析、データ構造のセットアップ、アクティベーション時のロジックの実行にユーザーが気付くレベルの時間を要します。 ここでは、アクティブ化のプロセスを 3 つのフェーズに分けて見ていきます。 また、各フェーズにかかる時間を短縮するためのヒントのほか、アプリの起動の各フェーズをユーザーが受け入れやすいものにするための手法も紹介します。
アクティブ化時間とは、ユーザーがアプリを起動してからアプリが機能するようになるまでの時間です。 これは、ユーザーがアプリから受ける第一印象となるため、非常に重要な時間です。 ユーザーは、システムやアプリから途切れることなく即座にフィードバックが返ってくることを求めます。 アプリの起動が遅いと、ユーザーはシステムやアプリが壊れている、あるいは設計がまずいと考えます。 さらに悪いことに、アプリのアクティブ化に時間がかかりすぎると、プロセス ライフタイム マネージャー (PLM) がアプリを強制終了させたり、ユーザーがアプリをアンインストールしたりすることもあります。
起動の各段階の概要
起動には多くの可動要素が関わっており、最適なユーザー エクスペリエンスを提供するには、それらすべてを正しく調整する必要があります。 ユーザーがアプリのタイルをクリックしてからアプリケーションのコンテンツが表示されるまでの間、次の手順が実行されます。
- Windows シェルがプロセスを開始し、Main が呼び出されます。
- Application オブジェクトが作成されます。
- (ProjectTemplate) コンストラクターが InitializeComponent を呼び出し、これにより App.xaml が解析され、オブジェクトが作成されます。
- Application.OnLaunched イベントが発生します。
- (ProjectTemplate) アプリ コードが Frame を作成し、MainPage に移動します。
- (ProjectTemplate) Mainpage コンストラクターが InitializeComponent を呼び出し、これにより MainPage.xaml が解析され、オブジェクトが作成されます。
- (ProjectTemplate) Window.Current.Activate() が呼び出されます。
- XAML プラットフォームが、測定と配置を含むレイアウト パスを実行します。
- ApplyTemplate によって、コントロールごとにコントロール テンプレートのコンテンツが作成されます。通常、これが起動のレイアウト時間の大部分を占めます。
- Render が呼び出されて、すべてのウィンドウのコンテンツのビジュアルが作成されます。
- デスクトップ ウィンドウ マネージャー (DWM) にフレームが提示されます。
起動パスでの処理を少なくする
最初のフレームに必要ないものを起動コード パスに含めないでください。
- 最初のフレームで必要とされないコントロールが含まれているユーザー dll がある場合は、それらの dll の読み込みを遅らせることを検討します。
- UI の一部がクラウドからのデータに依存している場合は、その UI を分割します。 まず、クラウド データに依存しない UI を起動し、次にクラウドに依存する UI を非同期的に起動します。 アプリケーションがオフラインで動作するように、またはアプリケーションが低速のネットワーク接続の影響を受けないように、データをローカルにキャッシュすることも検討する必要があります。
- UI がデータを待機している場合は、進行状況 UI を表示します。
- 構成ファイルの解析を多用するアプリの設計やコードによって動的に生成される UI に注意してください。
要素数の削減
XAML アプリの起動パフォーマンスは、起動時に作成する要素の数に直接関連しています。 作成する要素が少ないほど、アプリの起動にかかる時間が短くなります。 大まかな目安として、各要素の作成に 1 ミリ秒かかると考えてください。
- 項目コントロールで使用されるテンプレートは、何度も繰り返されるため、非常に大きい影響を与える可能性があります。 「ListView と GridView の UI の最適化」をご覧ください。
- UserControl とコントロール テンプレートは展開されるため、これらも考慮する必要があります。
- 画面に表示されない XAML を作成する場合は、起動時にそれらの XAML の作成が必要かどうかの根拠を確認する必要があります。
Visual Studio のライブ ビジュアル ツリー には、ツリー内の各ノードの子要素数が示されます。
延期を使用します。 要素を折りたたんだり、要素の不透明度を 0 に設定したりしても、要素が作成されるのを防ぐことはできません。 x:Load または x:DeferLoadStrategy を使用すると、UI の一部の読み込みを遅延させて、必要なときに読み込むことができます。 これは、起動画面に表示されない UI の処理を遅延させて、必要なときに、または遅延ロジックの一部としてその UI を読み込めるようにする良い方法です。 読み込みをトリガーするのに必要なことは、要素の FindName を呼び出すことだけです。 例と詳細については、「x:Load 属性」と「x:DeferLoadStrategy 属性」をご覧ください。
仮想化。 UI に一覧またはリピーター コンテンツがある場合は、UI の仮想化を利用することを強くお勧めします。 一覧の UI が仮想化されていないと、すべての要素の作成コストを前払いすることになり、それによって起動が遅くなる可能性があります。 「ListView と GridView の UI の最適化」をご覧ください。
アプリケーションのパフォーマンスを左右するのは、パフォーマンスそのものだけではありません。ユーザーの反応も重要な要素です。 操作の順序を変更して、最初に視覚的な変化が起きるようにすると、一般に、ユーザーはアプリケーションが速くなったと感じます。 画面にコンテンツが表示されると、ユーザーはアプリケーションが読み込まれたと見なします。 一般に、アプリケーションは起動の一環として複数の処理を実行する必要がありますが、そのすべてが UI の起動を必要とするわけではないため、そのような処理は遅延させるか、UI より優先順位を低くする必要があります。
このトピックでは、アニメーション/テレビに由来する「最初のフレーム」について説明していますが、これはコンテンツがエンド ユーザーに表示されるまでの時間を表す尺度です。
起動時の印象の改善
簡単なオンライン ゲームの例を使って、起動の各フェーズを特定し、プロセス全体を通じてユーザーにフィードバックを提供するためのさまざまな手法を紹介しましょう。 この例の場合、アクティブ化の最初のフェーズは、ユーザーがゲームのタイルをタップしてからゲームがコードの実行を開始するまでの時間です。 この間、システムには、正しいゲームが起動したことを示すためにユーザーに表示するコンテンツもありません。 ただし、スプラッシュ画面を提供することで、そのコンテンツをシステムに与えることができます。 次に、ゲームは、コードの実行を開始するときに静的なスプラッシュ画面をゲーム独自の UI に置き換えることで、アクティブ化の最初のフェーズが完了したことをユーザーに知らせます。
アクティブ化の 2 番目のフェーズでは、ゲームにとって重要な構造の作成と初期化を行います。 アクティブ化の最初のフェーズの後、利用できるデータを使ってアプリが初期 UI をすばやく作成できれば、2 番目のフェーズは単純であり、すぐに UI を表示できます。 それができない場合は、アプリの初期化中に読み込みページを表示することをお勧めします。
読み込みページはどのようなものでもかまいません。進行状況バーや進行状況リングを表示するだけのシンプルなものにすることもできます。 重要な点は、アプリがタスクを実行しており、まだ応答できないことを示すことです。 ゲームの場合は初期画面を表示しますが、その UI では画像とサウンドをディスクからメモリに読み込む必要があります。 これらのタスクには数秒かかるため、アプリはスプラッシュ画面を、ゲームのテーマに関連する簡単なアニメーションを表示する読み込みページに置き換えることで、ユーザーに情報を提供します。
読み込みページを置き換えるインタラクティブな UI の作成に必要な最小限の情報をゲームが取得すると、3 番目のフェーズが始まります。 この時点でオンライン ゲームが利用できる情報は、アプリがディスクから読み込んだコンテンツだけです。 インタラクティブな UI の作成に十分なコンテンツをゲームに搭載することもできますが、これはオンライン ゲームであるため、ゲームがインターネットに接続して追加の情報をダウンロードするまでゲームは機能しません。 機能するために必要な情報がすべて揃うまで、ユーザーは UI を操作できますが、Web からの追加データを必要とする機能については、コンテンツがまだ読み込み中であることを示すフィードバックをユーザーに提供する必要があります。 アプリが完全に機能するまで時間がかかる場合があるため、できるだけ早く機能を使えるようにすることが重要です。
これでオンライン ゲームのアクティブ化の 3 つのフェーズを特定したので、次にそれらを実際のコードに関連付けてみましょう。
フェーズ 1
アプリは起動する前に、スプラッシュ画面として表示したい内容をシステムに伝える必要があります。 それを行うには、例に示すように、アプリのマニフェストの SplashScreen 要素に画像と背景色を提供します。 アプリがアクティブ化を開始すると、その画像が Windows によって表示されます。
<Package ...>
...
<Applications>
<Application ...>
<VisualElements ...>
...
<SplashScreen Image="Images\splashscreen.png" BackgroundColor="#000000" />
...
</VisualElements>
</Application>
</Applications>
</Package>
詳しくは、「スプラッシュ画面の追加」をご覧ください。
アプリのコンストラクターは、アプリにとって重要なデータ構造を初期化する場合にのみ使用してください。 コンストラクターは、アプリを初めて実行するときにのみ呼び出されます。アプリがアクティブ化されるたびに必ず呼び出されるわけではありません。 たとえば、既に実行されており、バックグラウンドに配置されたアプリが検索コントラクトを介してアクティブ化された場合、コンストラクターは呼び出されません。
フェーズ 2
アプリはさまざまな理由でアクティブ化されますが、それぞれに異なる対応が必要になる場合があります。 OnActivated メソッド、OnCachedFileUpdaterActivated メソッド、OnFileActivated メソッド、OnFileOpenPickerActivated メソッド、OnFileSavePickerActivated メソッド、OnLaunched メソッド、OnSearchActivated メソッド、OnShareTargetActivated メソッドをオーバーライドして、さまざまなアクティブ化の理由に対応することができます。 アプリがこれらのメソッドで実行する必要がある作業の 1 つが UI を作成して Window.Content に割り当てた後、Window.Activate を呼び出すことです。 この時点で、スプラッシュ画面はアプリによって作成された UI に置き換えられます。 ここで表示するビジュアルは、読み込み画面でもいいし、アクティブ化時に UI を作成するのに十分な情報が利用できる場合は、アプリの実際の UI でもかまいません。
public partial class App : Application
{
// A handler for regular activation.
async protected override void OnLaunched(LaunchActivatedEventArgs args)
{
base.OnLaunched(args);
// Asynchronously restore state based on generic launch.
// Create the ExtendedSplash screen which serves as a loading page while the
// reader downloads the section information.
ExtendedSplash eSplash = new ExtendedSplash();
// Set the content of the window to the extended splash screen.
Window.Current.Content = eSplash;
// Notify the Window that the process of activation is completed
Window.Current.Activate();
}
// a different handler for activation via the search contract
async protected override void OnSearchActivated(SearchActivatedEventArgs args)
{
base.OnSearchActivated(args);
// Do an asynchronous restore based on Search activation
// the rest of the code is the same as the OnLaunched method
}
}
partial class ExtendedSplash : Page
{
// This is the UIELement that's the game's home page.
private GameHomePage homePage;
public ExtendedSplash()
{
InitializeComponent();
homePage = new GameHomePage();
}
// Shown for demonstration purposes only.
// This is typically autogenerated by Visual Studio.
private void InitializeComponent()
{
}
}
Partial Public Class App
Inherits Application
' A handler for regular activation.
Protected Overrides Async Sub OnLaunched(ByVal args As LaunchActivatedEventArgs)
MyBase.OnLaunched(args)
' Asynchronously restore state based on generic launch.
' Create the ExtendedSplash screen which serves as a loading page while the
' reader downloads the section information.
Dim eSplash As New ExtendedSplash()
' Set the content of the window to the extended splash screen.
Window.Current.Content = eSplash
' Notify the Window that the process of activation is completed
Window.Current.Activate()
End Sub
' a different handler for activation via the search contract
Protected Overrides Async Sub OnSearchActivated(ByVal args As SearchActivatedEventArgs)
MyBase.OnSearchActivated(args)
' Do an asynchronous restore based on Search activation
' the rest of the code is the same as the OnLaunched method
End Sub
End Class
Partial Friend Class ExtendedSplash
Inherits Page
Public Sub New()
InitializeComponent()
' Downloading the data necessary for
' initial UI on a background thread.
Task.Run(Sub() DownloadData())
End Sub
Private Sub DownloadData()
' Download data to populate the initial UI.
' Create the first page.
Dim firstPage As New MainPage()
' Add the data just downloaded to the first page
' Replace the loading page, which is currently
' set as the window's content, with the initial UI for the app
Window.Current.Content = firstPage
End Sub
' Shown for demonstration purposes only.
' This is typically autogenerated by Visual Studio.
Private Sub InitializeComponent()
End Sub
End Class
アクティブ化ハンドラーで読み込みページを表示するアプリは、バックグラウンドで UI の作成作業を開始します。 その要素が作成されると、FrameworkElement.Loaded イベントが発生します。 イベント ハンドラーでは、現在は読み込み画面になっているウィンドウのコンテンツを、新しく作成したホーム ページに置き換えます。
初期化時間が長いアプリの場合は読み込みページを表示することが非常に重要です。 アクティブ化プロセスに関するユーザー フィードバックの提供とは別に、アクティブ化プロセスの開始から 15 秒以内に Window.Activate が呼び出されないと、プロセスは終了します。
partial class GameHomePage : Page
{
public GameHomePage()
{
InitializeComponent();
// add a handler to be called when the home page has been loaded
this.Loaded += ReaderHomePageLoaded;
// load the minimal amount of image and sound data from disk necessary to create the home page.
}
void ReaderHomePageLoaded(object sender, RoutedEventArgs e)
{
// set the content of the window to the home page now that it's ready to be displayed.
Window.Current.Content = this;
}
// Shown for demonstration purposes only.
// This is typically autogenerated by Visual Studio.
private void InitializeComponent()
{
}
}
Partial Friend Class GameHomePage
Inherits Page
Public Sub New()
InitializeComponent()
' add a handler to be called when the home page has been loaded
AddHandler Me.Loaded, AddressOf ReaderHomePageLoaded
' load the minimal amount of image and sound data from disk necessary to create the home page.
End Sub
Private Sub ReaderHomePageLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
' set the content of the window to the home page now that it's ready to be displayed.
Window.Current.Content = Me
End Sub
' Shown for demonstration purposes only.
' This is typically autogenerated by Visual Studio.
Private Sub InitializeComponent()
End Sub
End Class
拡張スプラッシュ画面の使用例については、スプラッシュ画面のサンプルをご覧ください。
フェーズ 3
アプリに UI が表示されたからといって、アプリが完全に使える状態になったわけではありません。 このゲームの場合、UI は、インターネットからのデータを必要とする機能のプレースホルダーと共に表示されます。 この時点で、ゲームはアプリを完全に機能させるために必要な追加データをダウンロードし、データが取得されるにつれて機能を段階的に有効にします。
アクティブ化に必要なコンテンツの多くがアプリにパッケージ化されている場合があります。 単純なゲームがその例です。 パッケージ化により、アクティブ化プロセスは非常に簡単になります。 しかし、多くのプログラム (ニュース リーダー、フォト ビューアーなど) は Web から情報を取得しないと機能しません。 取得するデータが大きくなり、ダウンロードにかなりの時間がかかる場合があります。 アプリがアクティブ化プロセス中にこのデータをどのように取得するかは、ユーザーが体感するアプリのパフォーマンスに大きな影響を与える可能性があります。
アクティブ化のフェーズ 1 またはフェーズ 2 で、アプリを機能させるために必要なデータ セット全体をアプリがダウンロードしようとすると、読み込みページ、最悪の場合はスプラッシュ画面が数分間表示される可能性があります。 これにより、アプリは、ハングしたように見えるか、システムによって強制終了されます。 フェーズ 2 では、アプリがインタラクティブな UI を表示するための最小限のデータをプレースホルダー要素と共にダウンロードし、フェーズ 3 でプレースホルダー要素を置き換えるデータを段階的に読み込むようにすることをお勧めします。 データの処理について詳しくは、「ListView と GridView の最適化」をご覧ください。
起動の各フェーズにどれだけ細かく対応するかはまったくの任意ですが、できるだけ多くのフィードバック (スプラッシュ画面、読み込み画面、データの読み込み中の UI) をユーザーに提供することで、ユーザーはアプリとシステム全体の動作が速いと感じます。
起動パス内のマネージド アセンブリを最小限に抑える
多くの場合、再利用可能なコードは、プロジェクトに含まれるモジュール (DLL) の形式で提供されます。 これらのモジュールを読み込むには、ディスクにアクセスする必要があります。そのためのコストは累積する可能性があります。 これはコールド起動に最も大きな影響を与えますが、ウォーム起動にも影響を与える可能性があります。 C# と Visual Basic の場合は、CLR がアセンブリをオンデマンドで読み込むことで、そのコストをできるだけ遅らせようとします。 つまり、CLR は、実行されたメソッドがモジュールを参照するまでモジュールを読み込みません。 したがって、CLR が不要なモジュールを読み込まないように、スタートアップ コードではアプリの起動に必要なアセンブリだけを参照してください。 起動パスに不要な参照を含む未使用のコード パスがある場合は、それらのコード パスを別のメソッドに移動して、不要な読み込みを回避できます。
モジュールの読み込みを減らすもう 1 つの方法は、アプリ のモジュールを結合することです。 通常、1 つの大きなアセンブリを読み込む方が、2 つの小さなアセンブリを読み込むより時間がかかりません。 いつでもそれが可能なわけではないため、開発者の生産性やコードの再利用性に重大な違いが生じない場合にのみモジュールを結合する必要があります。 PerfView や Windows Performance Analyzer (WPA) などのツールを使用して、起動時にどのモジュールが読み込まれるかを確認できます。
効率的な Web 要求を行う
XAML、画像、アプリにとって重要なその他のファイルなど、アプリのコンテンツをローカルでパッケージ化することで、アプリの読み込み時間を大幅に短縮できます。 ディスク操作はネットワーク操作より高速です。 アプリの初期化時に特定のファイルが必要な場合は、そのファイルをリモート サーバーから取得するのではなく、ディスクから読み込むことで全体の起動時間を短縮できます。
ジャーナルとページ キャッシュを効率的に実行する
Frame コントロールは、ナビゲーション機能を提供します。 Frame コントロールは、ページへのナビゲーション (Navigate メソッド)、ナビゲーション ジャーナリング (BackStack/ForwardStack プロパティ、GoForward/GoBack メソッド)、ページ キャッシュ (Page.NavigationCacheMode)、およびシリアル化のサポート (GetNavigationState メソッド) を提供します。
Frame で注意が必要なパフォーマンスは、主にジャーナリングとページ キャッシュに関するものです。
Frame ジャーナル。 Frame.Navigate() を使ってページに移動すると、現在のページの PageStackEntry が Frame.BackStack コレクションに追加されます。 PageStackEntry は比較的小さいものですが、BackStack コレクションのサイズに制限は組み込まれていません。 ユーザーはループ内を移動して、このコレクションを無制限に増大させる可能性があります。
PageStackEntry には、Frame.Navigate() メソッドに渡されたパラメーターも含まれます。 Frame.GetNavigationState() メソッドが機能できるように、そのパラメーターをシリアル化可能なプリミティブ型 (int や string など) にすることをお勧めします。 ただし、このパラメーターは、大量のワーキング セットやその他のリソースを占めるオブジェクトを参照する可能性があり、BackStack の各エントリのコストはその分だけ高くなります。 たとえば、StorageFile をパラメーターとして使用できますが、それを行うと、BackStack は無数のファイルを開いたままにします。
したがって、ナビゲーション パラメーターを小さく保ち、BackStack のサイズを制限することをお勧めします。 BackStack は、標準ベクター (C# では IList、C++/CX では Platform::Vector) であるため、エントリを削除するだけでトリミングできます。
ページのキャッシュ。 既定では、Frame.Navigate メソッドを使ってページに移動すると、ページの新しいインスタンスがインスタンス化されます。 同様に、Frame.GoBack で前のページに戻ると、前のページの新しいインスタンスが割り当てられます。
ただし、Frame では、これらのインスタンス化を回避できるオプションのページ キャッシュを使用できます。 ページをキャッシュに保存するには、Page.NavigationCacheMode プロパティを使用します。 このモードを Required に設定するとページが強制的にキャッシュされ、Enabled に設定するとページのキャッシュが許可されます。 既定では、キャッシュ サイズは 10 ページですが、この値は Frame.CacheSize プロパティでオーバーライドできます。 Required ページはすべてキャッシュされ、Required ページの数が CacheSize より少ない場合は、Enabled ページもキャッシュできます。
ページ キャッシュは、インスタンス化を回避することでパフォーマンスを向上させ、その結果ナビゲーションのパフォーマンスを向上させることができます。 一方、ページ キャッシュは、過剰なキャッシュによってパフォーマンスを低下させる可能性もあり、その場合はワーキング セットが影響を受けます。
したがって、アプリケーションにとって適切なページ キャッシュを使用することをお勧めします。 たとえば、フレーム内に項目の一覧を表示するアプリがあって、項目をタップすると、フレームがその項目の詳細ページに移動するとします。 一覧ページは、おそらくキャッシュするように設定されるはずです。 すべての項目で詳細ページが同じである場合も、おそらくキャッシュするように設定されるはずです。 ただし、詳細ページの多様性が高い場合は、キャッシュをオフのままにした方がよいかもしれません。