ベスト プラクティス: 破棄可能な Windows SharePoint Services オブジェクトの使用

概要: Windows SharePoint Services オブジェクトを使用するときに、Microsoft .NET Framework のメモリ内にオブジェクトが保持されないようにするためのベスト プラクティスを説明します。(29 ページ)

Scott Harris (Microsoft Corporation)

Mike Ammerlaan (Microsoft Corporation)

Roger Lamb (Microsoft Corporation)

Stefan Goßner (Microsoft Corporation)

公開日: 2006 年 6 月

更新日: 2009 年 1 月

適用対象: Windows SharePoint Services 3.0、Windows SharePoint Services 2.0、Microsoft Office SharePoint Server 2007、Microsoft Office SharePoint Portal Server 2003

目次

  • 破棄可能な Windows SharePoint Services オブジェクトの使用方法の概要

  • 正しく破棄されなかったオブジェクトの検出

  • オブジェクトを確実に破棄するコーディング技法

  • SPSite オブジェクト

  • SPWeb オブジェクト

  • 破棄を必要とする他のオブジェクト

  • まとめ

  • その他の技術情報

関連するダウンロード: SharePoint Dispose Checker Tool

破棄可能な Windows SharePoint Services オブジェクトの使用方法の概要

Windows SharePoint Services 3.0 オブジェクト モデルのオブジェクトは Windows SharePoint Services データを処理するためのインターフェイスの役割をします。開発者は Windows SharePoint Services ストアからデータの読み取りや新しいデータの書き込みを行うために、このオブジェクト モデルを頻繁に使用します。

Windows SharePoint Services オブジェクト モデルには IDisposable インターフェイスを実装したオブジェクトが含まれています。これらのオブジェクトを使用するときには、Microsoft .NET Framework のメモリにオブジェクトが長時間保持されないように注意する必要があります。

具体的には、IDisposable を実装した SharePoint オブジェクトは、使用が完了したときに明示的に破棄する必要があります。

ユーザー設定の Web パーツを使用する SharePoint サイトなど、SharePoint オブジェクトを多用するシナリオでは、使用が完了した SharePoint オブジェクトを破棄しなかった場合に、通常と異なる次のような動作が発生する場合があります。

  • Windows SharePoint Services アプリケーション プールの頻繁なリサイクル (特に使用のピーク時)

  • アプリケーションのクラッシュ (デバッガーではヒープの破損として表示)

  • Microsoft インターネット インフォメーション サービス (IIS) のワーカー プロセスによるメモリの大量使用

  • システムおよびアプリケーションのパフォーマンスの低下

この記事では、IDispose を実装した SharePoint オブジェクトの処理と破棄の適切な手順について説明します。この記事で解説する問題点は、SharePoint Dispose Checker Tool でも検出されます。このツールは、無償でダウンロードして使用できるプログラムです。アセンブリを検査して、SharePoint オブジェクトの不適切な処理と破棄が原因でメモリ リークを引き起こすコーディングを見つけることができます。

破棄が必要な理由

Windows SharePoint Services のオブジェクトのうち、主に SPSite クラスおよび SPWeb クラスのオブジェクトの中には、マネージ オブジェクトとして作成されるものがいくつかあります。しかし、これらのオブジェクトは、アンマネージ コードおよびメモリを使用して大半の処理を実行します。オブジェクトの中で、マネージ コードの部分は、アンマネージ コードの部分に比べてわずかです。比率の小さいマネージ コードの部分は、ガベージ コレクターにとってメモリの圧迫とはならないため、ガベージ コレクターはこのオブジェクトをメモリからすぐには解放しません。オブジェクトが大量のアンマネージ メモリを使用することが原因で、上で説明した通常と異なる動作が生じる可能性があります。Windows SharePoint Services の IDisposable 実装オブジェクトを使用する呼び出し元アプリケーションでは、使用が終了した時点でこれらのオブジェクトを破棄する必要があります。ガベージ コレクターによって、メモリから自動的に解放することはできません。

正しく破棄されなかったオブジェクトの検出

次の質問を考慮することで、正しく破棄されなかったオブジェクトが存在するかどうかを調べることができます。

  1. アプリケーション プールのリサイクルが (特に負荷が大きいときに) 頻繁に行われますか (メモリのしきい値に達した時にアプリケーション プールのリサイクルが行われる設定になっている場合)?

    メモリのしきい値は 800 MB ~ 1.5 GB にする必要があります (RAM が 2 GB 以上の場合)。1 GB に近くなったときにアプリケーション プールのリサイクルが行われるように設定すると最良の結果が得られますが、色々な設定を試しながら、お使いの環境で最適な設定を判断してください。リサイクルの設定値が小さすぎると、アプリケーション プールのリサイクルが頻繁に発生するため、システムでパフォーマンスの問題が生じます。設定値が大きすぎると、ページのスワップ、メモリの断片化、その他の原因により、システムでパフォーマンスの問題が生じます。

  2. システムのパフォーマンスが (特に負荷が大きいときに) 低下しますか?

    メモリ使用量が増え始めると、システムではそれに対処するために、メモリのページングやメモリの断片化の処理などを行う必要が生じます。

  3. (特に負荷が大きいときに) システムがクラッシュしたり、タイムアウトやページ利用不可など、ユーザーの予期しないエラーが発生したりしますか?

    前述の通り、メモリ使用量が増えたりメモリが断片化されたりすると、他の操作にメモリを割り当てられなくなるために一部の関数が失敗します。多くの場合、コードで "メモリ不足" の例外を適切に処理できず、失敗や判断しにくいエラーが発生します。

  4. システムで、ユーザー設定のまたはサード パーティの Web パーツ、またはユーザー設定のアプリケーションを使用していますか?

    ユーザーが、SharePoint オブジェクトの破棄はガベージ コレクションによって自動的に実行されるものと想定して、これらの Web パーツでオブジェクトの破棄が必要かどうかやその理由を把握していない場合があります。しかしその想定は、常に正しいとは限りません。

4 番目の質問と、その他の 1 つ以上の質問の答えが「はい」だった場合、ご使用のユーザー設定コードでアイテムを適切に破棄していない可能性が高いと言えます。

上で説明した通常と異なる動作がサイトで見られる場合に、オブジェクトが正しく破棄されないことによるメモリ リークが原因かどうかを判断するには、ULS ログ (C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\LOGS) で SPRequest オブジェクトに関連したエントリを確認してください。SPSite および SPWeb の各インスタンスには、SPRequest オブジェクトへの参照が含まれており、さらにその中には、データベース サーバーとの通信を処理するアンマネージ COM オブジェクへの参照が含まれています。Windows SharePoint Services は、特定の各スレッドや並列スレッドに存在する SPRequest オブジェクトの数を監視し、以下の 3 つの状況では、有用なエントリをログに追加します。

  • SPRequest オブジェクトの総数が、設定されたしきい値を超えた場合。

  • SPRequest オブジェクトが、スレッドの終了後も存在し続ける場合。

  • SPRequest オブジェクトに対してガベージ コレクションが実行された場合。

1 つ目のシナリオはもっとも頻繁に起き、特にサイトで SPRequest オブジェクトのしきい値として既定の 8 を使用している場合によく生じます。SPRequest オブジェクトの数がこのしきい値を超えると、次のエントリが ULS ログに書き込まれます。

"スレッド スレッド数 で、多数の SPRequest オブジェクト (オブジェクト数) が現在解放されていない可能性があります。このオブジェクトまたはその親 (SPWeb オブジェクトまたは SPSite オブジェクトなど) が適切に破棄されていることを確認してください。このオブジェクトの割り当て ID: {GUID}"

最適なしきい値は、サイトやサイト上で実行しているアプリケーションの性質によって異なります。サイトでパフォーマンス上の問題が発生している場合は、インストール環境の ULS ログを確認して、サイトのアプリケーションで作成されている SPRequest オブジェクトの数を把握する必要があります。これにより、サイトとアプリケーションの設計で作成される SPRequest オブジェクトが多すぎるかどうかを判断できます。正しく破棄されていないオブジェクトがパフォーマンスの問題の原因でない 場合でも、大量の SPRequest オブジェクトの使用による全体のメモリ消費を減らすために、サイトやユーザー設定サイトのアプリケーションを再設計しなければならない場合があります。

既定のしきい値が小さすぎて適さないサイトも多いため、次のレジストリ サブキーを編集して、このしきい値を変更できます。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings

LocalSPRequestWarnCount = 目的のしきい値

オブジェクトを正しく破棄していないことが原因で SPRequest オブジェクトが大量に保持され、サイトのメモリ使用量が不必要に増加していることが確認できた場合には、具体的にどの部分でオブジェクトを正しく破棄していないかを、次の 2 つのエントリで見つけることができます。どちらのエントリのメッセージも、SharePoint オブジェクトを正しく破棄していないためにメモリが無駄になっていることを示しており、どちらも単一スレッドでの SPRequest オブジェクトの数と状態に関係しています。

  • "このスレッドの終了までに SPRequest オブジェクトが破棄されませんでした。システム リソースの無駄を防ぐには、このオブジェクトまたはその親 (SPSite または SPWeb など) を、使用後すぐに破棄します。オブジェクトはここで破棄されます。割り当て ID: {GUID}このオブジェクトがどこで割り当てられたかを確認するには、レジストリ キーを HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings で作成します。次に、SPRequestStackTrace という名前の新しい DWORD を作成し、このキーの値を 1 にします。"

    このメッセージは、スレッドの終了時点でまだ存在していた SPRequest オブジェクトが破棄されたことを示しています。

  • "SPRequest オブジェクトが、明示的な解放ではなく、ガベージ コレクターによって収集されました。システム リソースの無駄を防ぐには、このオブジェクトまたはその親 (SPSite または SPWeb など) を、使用後すぐに破棄します。割り当て ID: {GUID} このオブジェクトがどこで割り当てられたかを確認するには、レジストリ キーを HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings で作成します。次に、SPRequestStackTrace という名前の新しい DWORD を作成し、このキーの値を 1 にします。"

    このメッセージは、ガベージ コレクターが SPRequest オブジェクトを破棄したことを示しています。

問題の原因となっているコードを把握するには、割り当て ID を含むエントリをログで検索するか、または警告メッセージの指示に従って、次のサブキー設定をレジストリに追加します。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings SPRequestStackTrace = 1

このサブキー設定を行うと、これらの警告が発生したときに、元の SPRequest の割り当て (SPSite オブジェクトまたは SPWeb オブジェクトの作成時に発生) のスタック トレースがログに追加されます。

以下のセクションでは、オブジェクトを適切に廃棄するために使用できるいくつかのコーディング技法を説明します。

オブジェクトを確実に破棄するコーディング技法

ある種のコーディング技法を用いると、確実にオブジェクトを破棄することができます。これらの技法には、コードに以下を使用することが含まれます。

  • Dispose メソッド

  • using 句

  • try、catch、および finally ブロック

Dispose メソッドと Close メソッドの使用

Dispose メソッドと Close メソッドは、SPWeb オブジェクトと SPSite オブジェクトに対して、同じように機能します。Dispose メソッドはオブジェクトの Close メソッドを呼び出します。Close ではなく Dispose メソッドを呼び出すようにお勧めします。それは、SPWeb オブジェクトと SPSite オブジェクトは IDisposable インターフェイスを実装しており、標準 .NET Framework のガベージ コレクションは Dispose メソッドを呼び出してオブジェクトに関連付けられているリソースをメモリーから解放するためです。

using 句

Microsoft Visual C# の using ステートメントを使用して、IDisposable インターフェイスを実装している SharePoint オブジェクトを自動的に破棄することができます。

次にコード例を示します。

String str;

using(SPSite oSPsite = new SPSite("http://server"))
{
  using(SPWeb oSPWeb = oSPSite.OpenWeb())
   {
       str = oSPWeb.Title;
       str = oSPWeb.Url;
   }
}  

using ステートメントを活用してコードを非常にシンプルにすることができます。C# Reference (using ステートメント) で示されているように、共通言語ランタイムは using 句を try and finally ブロックに変換し、IDisposable インターフェイスを実装しているオブジェクトを自動的に削除します。ただし多くの場合、using ステートメントはお勧めできません。使用する場合は、ランタイムの実行内容を理解した上で注意して使用してください。次に示すコードは、ランタイムが finally ブロックを形成してオブジェクトを自動で破棄することを望まない場合の例を示しています。この場合、SPContext は SPWeb オブジェクトを返します。

// Do not do this. Dispose() is automatically called on SPWeb. 
using( SPWeb web = SPControl.GetContextWeb(HttpContext.Current)) { ... }

SPContext オブジェクトは SharePoint フレームワークによって管理されているので、コードで明示的に破棄しないでください。SPContext.Site、SPContext.Current.Site、SPContext.Web、および SPContext.Current.Web により返される、SPSite オブジェクトと SPWeb オブジェクトの場合も同様です。

注意

このベスト プラクティスを実行することで、SharePoint Dispose Checker Tool の SPDisposeCheckID_220 の問題に対応できます。

同じ行で SharePoint オブジェクト モデルの呼び出しを組み合わせて使用する際は、ランタイムの実行内容を必ず認識した上でよく注意して行ってください。このシナリオで発生するリークは非常に見つけにくい種類のものです。

次のコードの例では、SPSite オブジェクトはインスタンス化されますが、破棄されません。これは、ランタイムが OpenWeb により返される SPWeb オブジェクトしか破棄しないためです。

void CombiningCallsLeak()
{
    using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
    {
        // ... New SPSite will be leaked.
    } // SPWeb object web.Dispose() automatically called.
}

この問題は、using ステートメント内にもう 1 つステートメントをネストすることで解決できます。

void CombiningCallsBestPractice()
{
    using (SPSite siteCollection = new SPSite(SPContext.Current.Web.Url))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
        //Perform operations on site.
        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called.
}

SPSite オブジェクトに対して操作を全く実行しないのであれば、以下のコード例のように、もっと簡潔に記述することができます。

void CombiningCallsBestPractice()
{
    using (SPSite siteCollection = new SPSite(SPContext.Current.Web.Url))
    using (SPWeb web = siteCollection.OpenWeb())
        {
        //Perform operations on site.
        } // SPWeb object web.Dispose() automatically called; SPSite object 
          // siteCollection.Dispose() automatically called.
}

独自の try、catch、および finally ブロックを組み立てなければならない場合もあります。分かりやすい例として、例外を扱う必要があるために catch ブロックを含めなければならない場合があります。次のセクションでは、try、catch、および finally ブロックの使い分けと使用方法を示します。

try、catch、および finally ブロック

例外を処理する必要がある時はいつでも try、catch、および finally ブロックの使用は有効です。try/catch ブロック内にあるいずれかのコードに管理目的の finally 句があるべきです。こうすることで、IDisposable を実装するオブジェクトが確実に破棄されます。次のコード例では、コードに catch ブロックを組み入れて例外を処理するようにしています。catch ブロックは絶対に空にしないでください。また、破棄する前に null テストを行うことがベスト プラクティスです。

String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;

try
{
   oSPSite = new SPSite("http://server");
   oSPWeb = oSPSite.OpenWeb(..);

   str = oSPWeb.Title;
}
catch(Exception e)
{
   //Handle exception, log exception, etc.
}
finally
{
   if (oSPWeb != null)
     oSPWeb.Dispose();

   if (oSPSite != null)
      oSPSite.Dispose();
}

foreach ブロック内に破棄可能なオブジェクトを作成する時の潜在的なリークを防ぐために、Try ブロックと finally ブロック、または using ステートメントが必要になります。そのコード例を以下に示します。

public static void SPSiteCollectionForEachBestPractice()
{
     string sUrl = "http://spvm";
 
      using (SPSite siteCollectionOuter = new SPSite(sUrl))
     {
         SPWebApplication webApp = siteCollectionOuter.WebApplication;
         SPSiteCollection siteCollections = webApp.Sites;

                  SPSite siteCollectionInner = null;
                  foreach (siteCollectionInner in siteCollections)
             {
                      try  //Should be first statement after foreach.
                      {
                          Console.WriteLine(siteCollectionInner.Url);
                          //Exception occurs here.
                      }
                      finally
                      {
                          if(siteCollectionInner != null)
                          siteCollectionInner.Dispose();
                 }
             }
         }
     } // SPSite object siteCollectionOuter.Dispose() automatically called.
 }

try、catch、finally ブロックと using ステートメントを使用する Response.Redirect

finally ブロックは、try ブロックで Response.Redirect が呼び出された後に実行されます。Response.Redirect が最終的に ThreadAbortException を生成します。この例外が発生すると、ランタイムはすべての finally ブロックを実行してからスレッドを終了します。しかし、finally ブロックが unbounded の計算をしたり、ThreadAbortException をキャンセルしたりする可能性があるので、スレッドが必ず終了するとは限りません。そのため、プロセスのリダイレクトや転送が起きる前に、オブジェクトを破棄する必要があります。コードがリダイレクトする必要がある場合は、次のコード例と同様の方法でコードを実装してください。

String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;

try
{
   oSPSite = new SPSite("http://server");
   oSPWeb = oSPSite.OpenWeb(..);

   str = oSPWeb.Title;
   if(bDoRedirection)
   {
       if (oSPWeb != null)
          oSPWeb.Dispose();
    
       if (oSPSite != null)
          oSPSite.Dispose();

       Response.Redirect("newpage.aspx");
   }
}
catch(Exception e)
{
}
finally
{
   if (oSPWeb != null)
     oSPWeb.Dispose();

   if (oSPSite != null)
      oSPSite.Dispose();
}

using 句はランタイムに finally ブロックを作成するよう指示を出すので、Response.Redirect を using 句内で使用する場合は、必ずオブジェクトが適切に破棄されるようにしてください。次のコード例は、それをどのように行えるかを示しています。

using (SPSite oSPSite = new SPSite("http://server"))
using (SPWeb oSPWeb = oSPSite.OpenWeb(..))
{
    if (bDoRedirection)
        Response.Redirect("newpage.aspx");
}

長期間のオブジェクト保持を減らすための推奨事項

以下の一般的な推奨事項に従うことにより、SharePoint オブジェクトの長期間保持を減らすことができます。

  • new 演算子でオブジェクトを作成する場合は、作成するアプリケーションがオブジェクトを破棄するようにしてください。

    注意

    このベスト プラクティスを実行することで、SharePoint Dispose Checker Tool の SPDisposeCheckID_110 の問題に対応できます。

    良いコーディングの例 #1

    明示的な破棄

    void CreatingSPSiteExplicitDisposeNoLeak()
    {
        SPSite siteCollection = null;
        try
        {
            siteCollection = new SPSite("http://moss");
        }
        finally
        {
            if (siteCollection != null)
                siteCollection.Dispose();
        }
    }
    

    良いコーディングの例 #2

    自動的な破棄

    CreatingSPSiteWithAutomaticDisposeNoLeak()
    {
        using (SPSite siteCollection = new SPSite("http://moss"))
        {
        } // SPSite object siteCollection.Dispose() is called automatically.
    }
    
  • ほかの SPWeb オブジェクト (OpenWeb() など) を返す SharePoint メソッドにより作成されたアイテムは破棄してください。

    注意

    このベスト プラクティスを実行することで、SharePoint Dispose Checker Tool の SPDisposeCheckID_120 の問題に対応できます。

    良いコーディングの例

    void OpenWebNoLeak()
    {
        using (SPSite siteCollection = new SPSite("http://moss"))
        {
            using (SPWeb web = siteCollection.OpenWeb())
            {
            } // SPWeb object web.Dispose() automatically called.
        }  // SPSite object siteCollection.Dispose() automatically called.
    }
    
  • 複数のスレッドで SPRequest オブジェクトを共有しないでください (SPRequest オブジェクトへの参照が含まれるオブジェクト拡張も含む)。2 つ以上のスレッドで SPRequest オブジェクトを共有する、あるいは SPRequest オブジェクトを 1 つのスレッドで作成して別のスレッドで破棄するといったコーディング技法は、サポートされていません。つまり、静的変数内に SPRequest オブジェクトへの参照を保持するいかなるオブジェクトも保存できないということです。そのため、IDisposable を実装する SharePoint オブジェクト (SPWeb や SPSite など) を静的変数に保存しないでください。

SPSite オブジェクト

このセクションでは、返される新しい SPSite オブジェクトの破棄が必要な場合について説明します。

一般には、呼び出し元のアプリケーションが新しい SPSite のコンストラクター (シグネチャは任意) を使用した場合には、オブジェクトを使用し終わったときに Dispose() メソッドを呼び出す必要があります。SPSite オブジェクトを GetContextSite() から取得した場合には、呼び出し元のアプリケーションがこのオブジェクトを破棄しないようにする必要があります。SPWeb および SPSite オブジェクトはこの方法で派生した内部的なリストを保持しているため、このオブジェクトを破棄すると SharePoint オブジェクト モデルが予期せぬ動作をする可能性があります。ページの完了後に、Windows SharePoint Services が内部的にこのリストに対して順に処理を行い、オブジェクトを適切に破棄します。

SPSiteCollection クラス

ここでは、SPSiteCollection オブジェクトのメソッド、プロパティ、および演算子のうちで、返された SPSite オブジェクトをアクセス後にクローズする必要があるものについて説明します。

SPSiteCollection.Add メソッド

SPSiteCollection.Add メソッドは、新しい SPSite オブジェクトを作成して返します。 SPSiteCollection.Add メソッドから返された SPSite オブジェクトは破棄する必要があります。

注意

このベスト プラクティスでは、SharePoint Dispose Checker Tool で SPDisposeCheckID_240 として識別される問題に対処できます。

推奨されないコーディング

void SPSiteCollectionAddLeak()
{
    SPWebApplication webApp = new SPSite("http://moss").WebApplication;
    SPSiteCollection siteCollections = webApp.Sites;
    SPSite siteCollection = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\\User", 
      "roger.lamb@litwareinc.com");
    // SPSite siteCollection leak.
}

推奨されるコーディング

void SPSiteCollectionAddNoLeak()
{
    SPWebApplication webApp = new SPSite("http://moss").WebApplication;
    SPSiteCollection siteCollections = webApp.Sites;
    using (SPSite siteCollection = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\\User", 
      "roger.lamb@litwareinc.com"))
    {
    } // SPSite object siteCollection.Dispose() automatically called.
}

SPSiteCollection [ ] インデックス演算子

SPSiteCollection [] インデックス演算子は、アクセスごとに新しい SPSite オブジェクトを返します。SPSite インスタンスは、そのオブジェクトが既にアクセスされたものについても作成されます。次に示すコード例は、SPSite オブジェクトの適切でない廃棄方法を示します。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_230 として識別されている問題に対処します。

推奨されないコーディング #1

インデックス演算子を使用する

void SPSiteCollectionIndexerLeak()
{
    using (SPSite siteCollectionOuter = new SPSite("http://moss"))
    {
        SPWebApplication webApp = siteCollectionOuter.WebApplication;
        SPSiteCollection siteCollections = webApp.Sites;

        SPSite siteCollectionInner = siteCollections[0];
        // SPSite siteCollectionInner leak. 
    } // SPSite object siteCollectionOuter.Dispose() automatically called.
}

推奨されないコーディング #2

ForEach ループを使用する

void SPSiteCollectionForEachLeak()
{
    using (SPSite siteCollectionOuter = new SPSite("http://moss"))
    {
        SPWebApplication webApp = siteCollectionOuter.WebApplication;
        SPSiteCollection siteCollections = webApp.Sites;

        foreach (SPSite siteCollectionInner in siteCollections)
        {
            // SPSite siteCollectionInner leak.
        }
    } // SPSite object siteCollectionOuter.Dispose() automatically called.
}

推奨されるコーディング #1

インデックス演算子を使用する

void SPSiteCollectionIndexerNoLeak()
{
    using (SPSite siteCollectionOuter = new SPSite("http://moss"))
    {
        SPSite siteCollectionInner = null;
        try
        {
            SPWebApplication webApp = siteCollectionOuter.WebApplication;
            SPSiteCollection siteCollections = webApp.Sites;

            siteCollectionInner = siteCollections[0];
        }
        finally
        {
            if (siteCollectionInner != null)
                siteCollectionInner.Dispose();
        }
    } // SPSite object siteCollectionOuter.Dispose() automatically called.
}

推奨されるコーディング #2

ForEach ループを使用する

void SPSiteCollectionForEachNoLeak()
{
    using (SPSite siteCollectionOuter = new SPSite("http://moss"))
    {
        SPWebApplication webApp = siteCollectionOuter.WebApplication;
        SPSiteCollection siteCollections = webApp.Sites;

        foreach (SPSite siteCollectionInner in siteCollections)
        {
            try
            {
                // ...
            }
            finally
            {
                if(siteCollectionInner != null)
                    siteCollectionInner.Dispose();
            }
        }
    } // SPSite object siteCollectionOuter.Dispose() automatically called.
}

SPSite.AllWebs プロパティ (SPWebCollection)

このセクションでは、アクセスした後に SPWeb オブジェクトのクローズを必要とする AllWebs プロパティ コレクション内のメソッド、プロパティ、または演算子について説明します。

SPSite.AllWebs.Add メソッド

SPSite.AllWebs.Add メソッドは、SPWeb オブジェクトを作成して返します。SPSite.AllWebs.Add から返されるすべての SPWeb オブジェクトを破棄する必要があります。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_150 として識別されている問題に対処します。

推奨されないコーディング

void AllWebsAddLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        SPWeb web = siteCollection.AllWebs.Add("site-relative URL");
        // SPWeb object leaked.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

推奨されるコーディング

void AllWebsAddNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.AllWebs.Add("site-relative URL"))
        {
        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

SPWebCollection.Add メソッド

SPWebCollection.Add メソッドは、破棄する必要のある SPWeb オブジェクトを作成して返します。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_200 として識別されている問題に対処します。

推奨されないコーディング

void SPWebCollectionAddLeak(string strWebUrl)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            SPWebCollection webCollection = siteCollection.AllWebs; // No AllWebs leak just getting reference.
            SPWeb innerWeb = webCollection.Add(strWebUrl);  // Must dispose of innerWeb.
            // innerWeb leak.
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

推奨されるコーディング

void SPWebCollectionAddNoLeak(string strWebUrl)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            SPWebCollection webCollection = siteCollection.AllWebs; // No AllWebs leak just getting reference.
            using (SPWeb innerWeb = webCollection.Add(strWebUrl))
            {
                //...
            }
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

SPSite.AllWebs [ ] インデックス演算子

SPSite.AllWebs [] インデックス演算子は、アクセスされるたびに新しい SPWeb インスタンスを返します。オブジェクトが既にアクセスされている場合でも、そのオブジェクトはインデックス操作中に作成されます。正常にクローズされていない場合、次のコード例は SPWeb オブジェクトを .NET Framework ガベージ コレクターに残します。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_130 として識別されている問題に対処します。

推奨されないコーディング

void AllWebsForEachLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            foreach (SPWeb innerWeb in siteCollection.AllWebs)
            {
                // Explicitly dispose here to avoid out of memory leaks with large number of SPWeb objects.
            }
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

推奨されるコーディング #1

ForEach ループを使用する

void AllWebsForEachNoLeakOrMemoryOOM()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            foreach (SPWeb innerWeb in siteCollection.AllWebs)
            {
                try
                {
                    // ...
                }
                finally
                {
                    if(innerWeb != null)
                        innerWeb.Dispose();
                }
            }
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

推奨されるコーディング #2

インデックス演算子を使用する

void AllWebsIndexerNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.AllWebs[0])
        {
        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

SPSite.OpenWeb および SPSite. SelfServiceCreateSite メソッド

OpenWeb() メソッドおよび SelfServiceCreateSite() メソッド (すべてのシグネチャ) は、SPWeb オブジェクトを作成して、呼び出し元に返します。この新しいオブジェクトは SPSite オブジェクトには保存されず、SPSite クラスのどこにおいても破棄されません。このため、これらのメソッドによって作成されたすべてのオブジェクトを破棄する必要があります。

推奨されないコーディング

void OpenWebLeak()
{
    using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
    {
        // SPSite leaked !
    } // SPWeb object web.Dispose() automatically called.
}

推奨されるコーディング

void OpenWebNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called.
}

SPSite.RootWeb プロパティ

この記事の以前のバージョンでは、呼び出し元アプリケーションが、使用する SPSite オブジェクトを破棄する直前に、SPSite.RootWeb プロパティを破棄することが推奨されていました。これは正式な手順ではなくなりました。破棄によるクリーンアップは、SharePoint フレームワークによって自動的に処理されます。さらに、SPSite プロパティ LockIssue、Owner、および SecondaryContact は、内部で RootWeb プロパティを使用していました。RootWeb の更新された手順によると、これらのプロパティが使用されている場合はどんな場合でも、SPSite.RootWeb プロパティ上の Dispose メソッドを呼び出すことが推奨されなくなりました。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_140 として識別されている問題に対処します。

推奨されるコーディング

public void RootWebBestPractice()
{
    // New SPSite.
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        SPWeb rootWeb1 = siteCollection.RootWeb;
        // No explicit rootWeb1 dispose required.
    }  // siteCollection automatically disposed by implementing using().
    // rootWeb1 will be Disposed by SPSite.

    // SPContext and SPControl
    SPWeb rootWeb2 = SPContext.Current.Site.RootWeb;
    // Also would apply to SPControl.GetContextSite(Context);
    // No explicit rootWeb2 dispose required because it's obtained from SPContext.Current.Site.
}

Microsoft.Office.Server.UserProfiles.PersonalSite (Office SharePoint Server 2007 のみ)

Microsoft.Office.Server.UserProfiles.PersonalSite は、破棄する必要のある SPSite オブジェクトを返します。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_400 として識別されている問題に対処します。

推奨されないコーディング

void PersonalSiteLeak()
{
    // Open a site collection.
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
        UserProfile profile = profileManager.GetUserProfile("domain\\username");
        SPSite personalSite = profile.PersonalSite;    // Will leak.
    }
}

推奨されるコーディング

void PersonalSiteNoLeak()
{
    // Open a site collection.
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
        UserProfile profile = profileManager.GetUserProfile("domain\\username");
        using (SPSite personalSite = profile.PersonalSite)
        {
            // ...
        }
    }
}

別の稀なケースとして、UserProfiles.PersonalSite のリークが次のコード例に示されています。

void PersonalSiteLeak()
{
    // Open a site collection.
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
        UserProfile profile = profileManager.GetUserProfile("domain\\username");
        SPSite personalSite = profile.PersonalSite;    // Will leak.
    }
}

このようなリークを解決するには、次のコード例に示されているパターンに従ってください。

void PersonalSiteNoLeak()
{
    // Open a site collection
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
        UserProfile profile = profileManager.GetUserProfile("domain\\username");
        using (SPSite personalSite = profile.PersonalSite)
        {
            // ...
        }
    }
}

また、次のコード例に示されているように、ProfileLoader から PersonalSite オブジェクトを取得することにより、パフォーマンスを向上させる (SPSite オブジェクトの作成を避ける) ことができます。

UserProfile myProfile = ProfileLoader.GetProfileLoader().GetUserProfile();
using (SPSite personalSite = myProfile.PersonalSite)
{
     // ...
}

さらに、個人用サイトの Web パーツを作成する場合、破棄する必要のない PersonalSite のインスタンスを使用できます。

IPersonalPage currentMySitePage = this.Page as IPersonalPage;
if (currentMySitePage != null && !currentMySitePage.IsProfileError)
{
     SPSite personalSite = currentMySitePage.PersonalSite; // Will not leak.
     // ...
}

SPWeb オブジェクト

このセクションでは、SPWeb オブジェクトが返されて、破棄することが必要になる状況について説明します。

SPWeb.ParentWeb プロパティ

更新された手順

この記事の以前のバージョンでは、呼び出し元のアプリケーションが SPWeb.ParentWeb を破棄することが勧められていました。これは正式な手順ではなくなりました。破棄によるクリーンアップは、SharePoint フレームワークによって自動的に処理されます。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_170 として識別されている問題に対処します。

推奨されるコーディング

using (SPSite site = new SPSite(http://localhost)) 
{
    using (SPWeb web = site.OpenWeb())
    {
        SPList list = web.Lists["Announcements"];
        SPWeb parentWeb = list.ParentWeb; //No explicit dispose required.
    }
}

SPWeb.Webs プロパティ

このセクションでは、アクセスした後に SPWeb オブジェクトの破棄が必要な Webs プロパティ コレクションのメソッド、プロパティ、または演算子について説明します。

SPWeb.Webs

SPWeb.Webs プロパティは SPWebCollection オブジェクトを返します。このコレクション内の SPWeb オブジェクトを破棄する必要があります。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_180 として識別されている問題に対処します。

推奨されないコーディング

void WebsLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            foreach (SPWeb innerWeb in outerWeb.Webs)
            {
                // SPWeb innerWeb leak.
            }
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

推奨されるコーディング

void WebsNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            foreach (SPWeb innerWeb in outerWeb.Webs)
            {
                try //Should be first statement after foreach.
                {
                    // ...
                }
                finally
                {
                    if(innerWeb != null)
                        innerWeb.Dispose();
                }
            }
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

SPWeb.Webs.Add

SPWeb.Webs.Add メソッド (または Add()) は新しい SPWeb オブジェクトを作成して返します。このメソッドの呼び出しから返されたすべての SPWeb オブジェクトを破棄する必要があります。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_190 として識別されている問題に対処します。

推奨されないコーディング

void WebsAddLeak(string strWebUrl)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            SPWeb addedWeb = web.Webs.Add(strWebUrl);   // Will leak.

        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called.
}

推奨されるコーディング

void WebsAddNoLeak(string strWebUrl)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            using (SPWeb addedWeb = web.Webs.Add(strWebUrl))
            {
                //..
            }

        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called.
}

SPWeb.Webs[] インデックス演算子

SPWeb.Webs[] インデックス演算子は、アクセスごとに新しい SPWeb オブジェクトを返します。SPWeb は、 オブジェクトが既にアクセスされている場合でも、OpenWeb メソッドを呼び出すことによって作成されます。次のコード例は, .NET Framework が使用するメモリ内のこれらのオブジェクトを長期的に保持します。

推奨されないコーディング #1

For ループを使用する

int i;

SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);

oSPWeb = oSPSite.OpenWeb();

for(i = 0;i < oSPWeb.Webs.Count;i++)
{
   oSPWeb2 = oSPWeb.Webs[i];
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}

推奨されないコーディング #2

ForEach ループを使用する

SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);

oSPWeb = oSPSite.OpenWeb();

foreach(SPWeb oSPWeb2 in oSPWebe.Webs)
{
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}

推奨される修復は、各ループの終わりに破棄することです。

推奨されるコーディング #1

For ループを使用する

int i;

SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);

oSPWeb = oSPSite.OpenWeb();

for(i = 0;i < oSPWeb.Webs.Count;i++)
{
   oSPWeb2 = oSPWeb.Webs[i];
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
   oSPWeb2.Dispose();
}

oSPWeb.Dispose();

推奨されるコーディング #2

ForEach ループを使用する

SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);

oSPWeb = oSPSite.OpenWeb();

foreach(SPWeb oSPWeb2 in oSPWeb.Webs)
{
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
   oSPWeb2.Dispose();
}

oSPWeb.Dispose();

推奨されるコーディング #3

自動破棄を組み合わせて For ループを使用する

int i;

SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);

using(oSPWeb = oSPSite.OpenWeb())
{
   for(i = 0;i < oSPWeb.Webs.Count;i++)
   {
      Using(oSPWeb2 = oSPWeb.Webs[i])
      {
         BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
      }
   }
}

破棄を必要とする他のオブジェクト

このセクションでは、他の SharePoint オブジェクト上の Dispose メソッドを呼び出す場合について説明します。

Microsoft.SharePoint.Portal.SiteData.Area.Web プロパティ

Web プロパティは、アクセスされるたびに新しい SPWeb オブジェクトを返します。すべての Area.Web プロパティの使用には、Dispose メソッドへの対応する呼び出しが必要です。Area および AreaManager クラスは Office SharePoint Server 2007 で廃止されていますが、依然としてレガシー コードを移行する際の問題となっています。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_500 として識別されている問題に対処します。

推奨されないコーディング

void AreaWebLeak()
{
    // AreaManager and Area are obsolete in SharePoint Server, but this
    // should still be noted.
    Area area = AreaManager.GetArea(PortalContext.Current, new Guid("{GUID}"));
    string str = area.Web.Title;
    // SPWeb area.Web leak.
}

推奨されるコーディング

public void AreaWebNoLeak()
{
    // AreaManager and Area are obsolete in MOSS but this should still be noted
    Area area = AreaManager.GetArea(PortalContext.Current, new Guid("{GUID}"));
    using (SPWeb areaWeb = area.Web)
    {
        string str = areaWeb.Title;
    }
}

SPControl.GetContextSite および SPControl.GetContextWeb メソッド

SharePoint コンテキスト オブジェクト (GetContextSite() メソッドおよび GetContextWeb() メソッド) からオブジェクトを取得した場合、呼び出し元アプリケーションが、オブジェクト上の Dispose メソッドを呼び出さないようにしてください。そのようにすると、SharePoint オブジェクト モデルが予期しない動作をするか失敗します。これは、このようにして派生する SPSite および SPWeb オブジェクトに保持されている内部リストが原因です。ページの完了後に、オブジェクト モデルは、このリストを内部的に順に処理して、オブジェクト プロパティを適切に破棄します。

これらのオブジェクトで作成されたオブジェクトを破棄する必要があります。例えば、GetContextSite() メソッドを使用して取得した SPSite オブジェクトから Web サイトを開いた場合です。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_210 として識別されている問題に対処します。

推奨されないコーディング

void SPControlBADPractice()
{
    SPSite siteCollection = SPControl.GetContextSite(Context);
    siteCollection.Dispose();   // DO NOT DO THIS
    SPWeb web = SPControl.GetContextWeb(Context);
    web.Dispose();  // DO NOT DO THIS.
}

推奨されるコーディング

void SPControlBestPractice()
{
    SPSite siteCollection = SPControl.GetContextSite(Context);
    SPWeb web = SPControl.GetContextWeb(Context);
    // Do NOT call Dispose().
}

Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager

SPLimitedWebPartManager クラスには、破棄する必要のある内部の SPWeb オブジェクトへの参照が含まれます。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_160 として識別されている問題に対処します。

推奨されないコーディング

void SPLimitedWebPartManagerLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            SPFile page = web.GetFile("Source_Folder_Name/Source_Page");
            SPLimitedWebPartManager webPartManager =
                page.GetLimitedWebPartManager(PersonalizationScope.Shared);
            // SPWeb object webPartManager.Web leaked.
        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

推奨されるコーディング

void SPLimitedWebPartManagerLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            SPFile page = web.GetFile("Source_Folder_Name/Source_Page");
            SPLimitedWebPartManager webPartManager =
                page.GetLimitedWebPartManager(PersonalizationScope.Shared);
                webPartManaber.Web.Dispose();
        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

Microsoft.SharePoint.Publishing.PublishingWeb (SharePoint Server 2007 のみ)

PublishingWeb クラスの GetPublishingWebs メソッドは PublishingWebCollection オブジェクトを返します。列挙されているそれぞれの innerPubWeb オブジェクト上で Close メソッドを呼び出す必要があります。GetPublishingWeb メソッドのみを呼び出す場合、Close を呼び出す必要はありません。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_300 として識別されている問題に対処します。

推奨されないコーディング

void PublishingWebCollectionLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            // Passing in SPWeb object that you own, no dispose needed on
            // outerPubWeb.
            PublishingWeb outerPubWeb = PublishingWeb.GetPublishingWeb(web);

            PublishingWebCollection pubWebCollection = outerPubWeb.GetPublishingWebs();
            foreach (PublishingWeb innerPubWeb in pubWebCollection)
            {
                // innerPubWeb leak.
            }
            // PublishingWeb will leak for each innerPubWeb referenced
        } // SPWeb object web.Dispose() automatically called.
    } // SPSite object siteCollection.Dispose() automatically called.
}

推奨されるコーディング

void PublishingWebCollectionNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            // Passing in SPWeb object that you own, no dispose needed on
            // outerPubWeb.
            PublishingWeb outerPubWeb = PublishingWeb.GetPublishingWeb(web);
            PublishingWebCollection pubWebCollection = outerPubWeb.GetPublishingWebs();
            foreach (PublishingWeb innerPubWeb in pubWebCollection)
            {
                try
                {
                    // ...
                }
                finally
                {
                    if(innerPubWeb != null)
                        innerPubWeb.Close();
                }
            }
        }  // SPWeb object web.Dispose() automatically called.
    } // SPSite object siteCollection.Dispose() automatically called.
}

注意

Microsoft.SharePoint.Publishing.PublishingWebCollection.GetPublishingWebs によって返される PublishingWebCollection に対して Add メソッドを呼び出すことによって作成される各 PublishingWeb オブジェクトについても、同様に Close を呼び出す必要があります。コード例については、Microsoft Office SharePoint Server 2007 SDKGetPublishingWebs() メソッドを参照してください。このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_310 として識別されている問題に対処します。

Microsoft.SharePoint.Publishing.PublishingWeb.GetVariation メソッドは、破棄する必要のある PublishingWeb オブジェクトを返します。

注意

このベスト プラクティスは、SharePoint Dispose Checker Tool で SPDisposeCheckID_320 として識別されている問題に対処します。

推奨されないコーディング

void GetVariationLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);  // Passing in SPWeb object, so no Close() needed
            VariationLabel variationLabel = Variations.Current.UserAccessibleLabels[0];
            PublishingWeb variationPublishingWeb = publishingWeb.GetVariation(variationLabel);  // Must be Closed().
            // ...
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

推奨されるコーディング

void GetVariationNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            PublishingWeb variationPublishingWeb = null;
            try
            {
                PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);  // Passing in SPWeb object, so no Close() needed.
                VariationLabel variationLabel = Variations.Current.UserAccessibleLabels[0];
                variationPublishingWeb = publishingWeb.GetVariation(variationLabel);  // Must be Closed().
                // ...
            }
            finally
            {
                if(variationPublishingWeb != null)
                    variationPublishingWeb.Close();
            }
        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}

複数メソッド間の破棄パターン

次の例は、クラス内の複数メソッド間で SPSite および SPWeb オブジェクトを保持するための一般的な方法を示します。このデザイン パターンが必要な場合もありますが、複数のメソッドにまたがる呼び出しの完了後の適切なタイミングで Dispose を呼び出すようにしてください。次のコード例は、クラスが範囲外になる際のSPSite および SPWeb のリークのパターンを示しています。

public class CrossMethodLeak
{
    private SPSite _siteCollection = null;
    private SPWeb _web = null;

    public void MethodA()
    {
        _siteCollection = new SPSite("http://moss");
        _web = _siteCollection.OpenWeb();
    }

    public void MethodB()
    {
        if (_web != null)
        {
            string title = _web.Title;
        }
    }

    public void MethodC()
    {
        if (_web != null)
        {
            string name = _web.Name;
        }
    }
}

まとめ

多くの SharePoint オブジェクトが IDisposable インターフェイスを実装しているため、これらのオブジェクトを使用する際に、それらがメモリに残されないように十分注意する必要があります。この記事に記載されている SharePoint オブジェクトの破棄に関する手順に従うなら、Windows SharePoint Services ユーザー設定コードの信頼性を向上させるのに役立ちます。

謝辞

このドキュメントの作成にあたって、次の方々のご協力とご支援に感謝申し上げます。

  • Keith Richie

  • Rob Anderson (Microsoft Corporation)

  • Steve Sheppard (Microsoft Corporation)

  • Chris Gideon (Microsoft Corporation)

  • Kelvin Lo (Microsoft Corporation)

  • Rashid Aga (Microsoft Corporation)

その他の技術情報