オブジェクトの破棄
最終更新日: 2010年4月8日
適用対象: SharePoint Foundation 2010
この記事の内容
破棄可能な SharePoint オブジェクトの使用方法の概要
正しく破棄されなかったオブジェクトの検出
オブジェクトを正しく破棄するためのコーディング手法
SPSite オブジェクト
SPWeb オブジェクト
破棄が必要なその他のオブジェクト
まとめ
破棄可能な SharePoint オブジェクトの使用方法の概要
Microsoft SharePoint Foundation 2010 および Microsoft SharePoint Server 2010 のオブジェクト モデルのオブジェクトは、SharePoint Foundation のデータを処理するためのインターフェイスの役割を果たします。開発者は、SharePoint Foundation 2010 および SharePoint Server 2010 のデータ ストアとの間でデータの読み取りや新しいデータの書き込みを行うために、オブジェクト モデルを頻繁に使用します。
SharePoint Foundation 2010 および SharePoint Server 2010 のオブジェクト モデルには、IDisposable インターフェイスを実装したオブジェクトが含まれています。これらのオブジェクトを使用するときには、Microsoft .NET Framework のメモリにオブジェクトが長時間保持されることがないように注意が必要です。
具体的には、IDisposable を実装した SharePoint オブジェクトは、使用が完了したときに明示的に破棄する必要があります。
カスタム Web パーツを使用する SharePoint サイトなど、SharePoint オブジェクトを多用するシナリオでは、使用が完了した SharePoint オブジェクトを破棄しなかった場合に、通常と異なる次のような動作が発生する場合があります。
SharePoint Foundation アプリケーション プールの頻繁なリサイクル (特に使用のピーク時)
アプリケーションのクラッシュ (デバッガーではヒープの破損として表示)
インターネット インフォメーション サービス (IIS) のワーカー プロセス用のメモリの大量使用
システムおよびアプリケーションのパフォーマンスの低下
この記事では、IDispose を実装した SharePoint オブジェクトの処理と破棄の適切な手順について説明します。この記事で解説する問題点は、SharePoint Dispose Checker Tool (英語) でも検出されます。このツールは、無償でダウンロードして使用できるプログラムです。アセンブリを検査して、SharePoint オブジェクトの不適切な処理と破棄が原因でメモリ リークを引き起こすコーディングを見つけることができます。
破棄が必要な理由
SharePoint Foundation のオブジェクトのうち、主に SPSite クラスおよび SPWeb クラスのオブジェクトの中には、マネージ オブジェクトとして作成されるものがいくつかあります。しかし、これらのオブジェクトは、アンマネージ コードおよびメモリを使用して大半の処理を実行します。オブジェクトの中で、マネージ コードの部分は、アンマネージ コードの部分に比べてわずかです。比率の小さいマネージ コードの部分は、ガベージ コレクターにとってメモリの圧迫とはならないため、ガベージ コレクターはこのオブジェクトをメモリからすぐには解放しません。オブジェクトが大量のアンマネージ メモリを使用することが原因で、上で説明した通常と異なる動作が生じる可能性があります。SharePoint Foundation の IDisposable 実装オブジェクトを使用する呼び出し元アプリケーションでは、使用が終了した時点でこれらのオブジェクトを破棄する必要があります。ガベージ コレクターに任せてメモリから自動で解放させることはできません。
正しく破棄されなかったオブジェクトの検出
次の質問について考えると、正しく破棄されないオブジェクトが存在する可能性を判断できます。
負荷が大きい状態のときなどに、アプリケーション プールのリサイクルが頻繁に行われるかどうか (メモリのしきい値に達したときにリサイクルするようにアプリケーション プールを設定していると仮定した場合)。
RAM が 2 GB 以上と仮定した場合、メモリのしきい値は 800 MB ~ 1.5 GB に設定する必要があります。1 GB 付近でアプリケーション プールのリサイクルが行われるように設定すると最適な結果が得られますが、各自の環境における最も適切な設定については実験により判断します。リサイクルの設定値が小さすぎる場合、アプリケーション プールのリサイクルが頻繁に発生するため、システムのパフォーマンスに問題が生じます。設定値が大きすぎる場合、ページのスワップ、メモリの断片化、およびその他の問題によって、システムのパフォーマンスに問題が生じます。
システムのパフォーマンスが低下するかどうか (特に負荷が大きいとき)。
メモリ使用量が増加し始めると、システムはその対処のために、たとえばメモリのページングやメモリ断片化の処理が必要になります。
システムがクラッシュするかどうか、または、タイムアウト、ページ利用不可など、ユーザーが予期しないエラーが生じるかどうか (特に負荷が大きいとき)。
上記と同様、メモリ使用量が増えたりメモリが断片化したりすると、他の操作に必要なメモリを割り当てられないことが原因で、一部の関数が失敗します。多くの場合、コードで "メモリ不足" の例外を適切に処理しておらず、誤ったエラーや判断しにくいエラーにつながります。
システムで、カスタムまたはサード パーティの Web パーツ、またはカスタム アプリケーションを使用しているかどうか。
ユーザーが、SharePoint オブジェクトの破棄はガベージ コレクションによって自動的に実行されるものと想定して、これらの Web パーツでオブジェクトの破棄が必要かどうかやその理由を把握していない場合があります。しかしその想定は、常に正しいとは限りません。
上記の 4. の質問の答えが "はい" で、その他の質問にも 1 つ以上 "はい" がある場合には、カスタム コードでアイテムを適切に破棄していない可能性が十分あります。
上で説明した通常と異なる動作がサイトで見られる場合に、オブジェクトが正しく破棄されていないことによるメモリ リークが原因かどうかを判断するには、ULS ログ (場所は C:\Program Files\Common Files\microsoft shared\Web Server Extensions\14\LOGS) で SPRequest オブジェクトに関連するエントリを確認します。SPSite および SPWeb の各インスタンスには、SPRequest オブジェクトへの参照が含まれており、さらにその中には、データベース サーバーとの通信を処理するアンマネージ COM オブジェクトへの参照が含まれています。SharePoint Foundation は、個別の各スレッドおよび並列スレッドに存在する SPRequest オブジェクトの数を監視し、次の 3 つのシナリオで、有用なエントリをログに追加します。
SPRequest オブジェクトの総数が、設定されたしきい値を超えたとき
スレッドの最後で SPRequest オブジェクトが引き続き存在するとき
ガベージ コレクションによって SPRequest オブジェクトがヒープから削除されたとき
最初のシナリオは最も多く見られ、特に 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 メソッドの使用の比較
SPWeb オブジェクトおよび SPSite オブジェクトに対する Dispose メソッドと Close メソッドは同じように機能します。Dispose メソッドを呼び出すと、そのオブジェクトの Close メソッドが呼び出されます。Close ではなく Dispose メソッドを呼び出すことをお勧めします。SPWeb オブジェクトおよび SPSite オブジェクトは IDisposable インターフェイスを実装しており、標準の .NET Framework のガベージ コレクションは、オブジェクトに対応するリソースをメモリから解放するときに Dispose メソッドを呼び出すからです。
using ステートメント
Microsoft Visual C# および Visual Basic の using ステートメントを使用すると、IDisposable インターフェイスを実装した SharePoint オブジェクトを自動で破棄できます。
以下のコードはその例です。
String str;
using(SPSite oSPsite = new SPSite("https://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;
}
}
Dim str As String
Using oSPsite As New SPSite("https://server")
Using oSPWeb As SPWeb = oSPSite.OpenWeb()
str = oSPWeb.Title
str = oSPWeb.Url
End Using
End Using
using ステートメントを使用すると、コードを大幅に簡素化できます。C# リファレンス (「using Statement (英語)」) に説明があるように、共通言語ランタイムでは、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)) { ... }
' Do not do this. Dispose() is automatically called on SPWeb.
Using web As SPWeb = SPControl.GetContextWeb(HttpContext.Current)
'.......
End Using
SPContext オブジェクトは SharePoint フレームワークによって管理されており、コードで明示的に破棄しないようにする必要があります。同じことは、SPContext.Site、SPContext.Current.Site、SPContext.Web、および SPContext.Current.Web が返す SPSite オブジェクトおよび SPWeb オブジェクトについても言えます。
注意
このベスト プラクティスでは、SharePoint Dispose Checker Tool (英語) で SPDisposeCheckID_220 として識別される問題に対処できます。
SharePoint オブジェクト モデルの呼び出しを 1 つの行にまとめたときにランタイムが行う処理については、注意と理解が必要です。このシナリオによってメモリ リークが発生すると、検出するのが困難になります。
次のコード例では、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.
}
Private Sub CombiningCallsLeak()
Using web As SPWeb = New SPSite(SPContext.Current.Web.Url).OpenWeb()
' ... New SPSite will be leaked.
End Using ' SPWeb object web.Dispose() automatically called.
End Sub
この問題を修正するには、using ステートメントどうしを入れ子にします。
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.
}
Private Sub CombiningCallsBestPractice()
Using siteCollection As New SPSite(SPContext.Current.Web.Url)
Using web As SPWeb = siteCollection.OpenWeb()
' Perform operations on site.
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
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.
}
Private Sub CombiningCallsBestPractice()
Using siteCollection As New SPSite(SPContext.Current.Web.Url)
Using web As SPWeb = siteCollection.OpenWeb()
' Perform operations on site.
End Using ' SPWeb object web.Dispose() automatically called; SPSite object
End Using
' siteCollection.Dispose() automatically called.
End Sub
その他の場合には、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("https://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();
}
Dim str As String
Dim oSPSite As SPSite = Nothing
Dim oSPWeb As SPWeb = Nothing
Try
oSPSite = New SPSite("https://server")
oSPWeb = oSPSite.OpenWeb(..)
str = oSPWeb.Title
Catch e As Exception
' Handle exception, log exception, etc.
Finally
If oSPWeb IsNot Nothing Then
oSPWeb.Dispose()
End If
If oSPSite IsNot Nothing Then
oSPSite.Dispose()
End If
End Try
破棄可能なオブジェクトを 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.
}
Public Shared Sub SPSiteCollectionForEachBestPractice()
Dim sUrl As String = "http://spvm"
Using siteCollectionOuter As New SPSite(sUrl)
Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
Dim siteCollectionInner As SPSite = Nothing
For Each siteCollectionInner In siteCollections
Try ' Should be first statement after foreach.
Console.WriteLine(siteCollectionInner.Url)
' Exception occurs here.
Finally
If siteCollectionInner IsNot Nothing Then
siteCollectionInner.Dispose()
End If
End Try
Next
End Using
End Sub ' SPSite object siteCollectionOuter.Dispose() automatically called.
try、catch、および finally ブロックと using ステートメントでの Response.Redirect
finally ブロックは、try ブロック内の Response.Redirect の呼び出しの後で実行されます。Response.Redirect では、最終的に ThreadAbortException 例外が生成されます。この例外が発生した場合、ランタイムはすべての finally ブロックを実行してからスレッドを終了します。しかし、finally ブロックで無制限の計算や ThreadAbortException のキャンセルが行われる可能性があるため、スレッドは必ずしも終了するとは限りません。したがって、処理のリダイレクトや転送を行う前に、オブジェクトを破棄する必要があります。コードでリダイレクトが必要な場合には、次のコード例のような形で実装します。
String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;
try
{
oSPSite = new SPSite("https://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();
}
Dim str As String
Dim oSPSite As SPSite = Nothing
Dim oSPWeb As SPWeb = Nothing
Try
oSPSite = New SPSite("https://server")
oSPWeb = oSPSite.OpenWeb(..)
str = oSPWeb.Title
If bDoRedirection Then
If oSPWeb IsNot Nothing Then
oSPWeb.Dispose()
End If
If oSPSite IsNot Nothing Then
oSPSite.Dispose()
End If
Response.Redirect("newpage.aspx")
End If
Catch e As Exception
Finally
If oSPWeb IsNot Nothing Then
oSPWeb.Dispose()
End If
If oSPSite IsNot Nothing Then
oSPSite.Dispose()
End If
End Try
using ステートメントを使用すると、ランタイムによって finally ブロックが作成されます。このため、Response.Redirect を using ステートメント内で使用するときには、オブジェクトが適切に破棄されるようにする必要があります。次のコード例はその方法です。
using (SPSite oSPSite = new SPSite("https://server"))
using (SPWeb oSPWeb = oSPSite.OpenWeb(..))
{
if (bDoRedirection)
Response.Redirect("newpage.aspx");
}
Using oSPSite As New SPSite("https://server")
Using oSPWeb As SPWeb = oSPSite.OpenWeb(..)
If bDoRedirection Then
Response.Redirect("newpage.aspx")
End If
End Using
End Using
オブジェクトの長時間の保持を減らすための推奨事項
次の一般的な推奨事項に従うことで、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(); } }
Private Sub CreatingSPSiteExplicitDisposeNoLeak() Dim siteCollection As SPSite = Nothing Try siteCollection = New SPSite("http://moss") Finally If siteCollection IsNot Nothing Then siteCollection.Dispose() End If End Try End Sub
適切なコーディング方法 2
自動での破棄
CreatingSPSiteWithAutomaticDisposeNoLeak() { using (SPSite siteCollection = new SPSite("http://moss")) { } // SPSite object siteCollection.Dispose() is called automatically. }
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. }
Private Sub OpenWebNoLeak() Using siteCollection As New SPSite("http://moss") Using web As SPWeb = siteCollection.OpenWeb() End Using ' SPWeb object web.Dispose() automatically called. End Using ' SPSite object siteCollection.Dispose() automatically called. End Sub
SPRequest オブジェクト (および SPRequest オブジェクトへの参照を保持するオブジェクト) をスレッド間で共有しないでください。SPRequest オブジェクトを複数のスレッド間で共有するコーディング手法、および SPRequest オブジェクトを作成元とは別のスレッドで破棄するコーディング手法はサポートされていません。つまり、SPRequest オブジェクトへの参照を保持するオブジェクトを静的変数に格納することはできません。したがって、IDisposable を実装した SharePoint オブジェクト (SPWeb、SPSite など) を静的変数に格納しないでください。
SPSite オブジェクト
ここでは、新しく作成して取得した SPSite オブジェクトを破棄する必要がある状況について説明します。
一般には、呼び出し元のアプリケーションが new と SPSite のコンストラクター (シグネチャは任意) を使用した場合には、オブジェクトを使用し終わったときに Dispose() メソッドを呼び出す必要があります。SPSite オブジェクトを GetContextSite() から取得した場合には、呼び出し元のアプリケーションはこのオブジェクトを破棄しないでください。SPWeb オブジェクトおよび SPSite オブジェクトはこうして派生した内部的なリストを保持しているため、オブジェクトを破棄すると SharePoint オブジェクト モデルが予期せぬ動作を行う可能性があります。ページの完了後に、SharePoint Foundation が内部的にこのリストに対して順に処理を行い、オブジェクトを適切に破棄します。
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.
}
Private Sub SPSiteCollectionAddLeak()
Dim webApp As SPWebApplication = New SPSite("http://moss").WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
Dim siteCollection As SPSite = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\User", "roger.lamb@litwareinc.com")
' SPSite siteCollection leak.
End Sub
適切なコーディング方法
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.
}
Private Sub SPSiteCollectionAddNoLeak()
Dim webApp As SPWebApplication = New SPSite("http://moss").WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
Using siteCollection As SPSite = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\User", "roger.lamb@litwareinc.com")
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
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.
}
Private Sub SPSiteCollectionIndexerLeak()
Using siteCollectionOuter As New SPSite("http://moss")
Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
Dim siteCollectionInner As SPSite = siteCollections(0)
' SPSite siteCollectionInner leak.
End Using ' SPSite object siteCollectionOuter.Dispose() automatically called.
End Sub
不適切なコーディング方法 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.
}
Private Sub SPSiteCollectionForEachLeak()
Using siteCollectionOuter As New SPSite("http://moss")
Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
For Each siteCollectionInner As SPSite In siteCollections
' SPSite siteCollectionInner leak.
Next siteCollectionInner
End Using ' SPSite object siteCollectionOuter.Dispose() automatically called.
End Sub
適切なコーディング方法 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.
}
Private Sub SPSiteCollectionIndexerNoLeak()
Using siteCollectionOuter As New SPSite("http://moss")
Dim siteCollectionInner As SPSite = Nothing
Try
Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
siteCollectionInner = siteCollections(0)
Finally
If siteCollectionInner IsNot Nothing Then
siteCollectionInner.Dispose()
End If
End Try
End Using ' SPSite object siteCollectionOuter.Dispose() automatically called.
End Sub
適切なコーディング方法 2
foreach ループの使用
void SPSiteCollectionForEachNoLeak()
{
using (SPSite siteCollectionOuter = new SPSite("http://yoursite”))
{
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.
}
Private Sub SPSiteCollectionForEachNoLeak()
Using siteCollectionOuter As SPSite = New SPSite("http://yoursite")
Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
For Each siteCollectionInner As SPSite In siteCollections
Try
' ...
Finally
If siteCollectionInner IsNot Nothing Then
siteCollectionInner.Dispose()
End If
End Try
Next
End Using
End Sub
SPSite.AllWebs プロパティ (SPWebCollection)
ここでは、AllWebs プロパティ コレクションのメソッド、プロパティ、および演算子のうちで、アクセスの後で SPWeb オブジェクトをクローズする必要があるものについて説明します。
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.
}
Private Sub AllWebsAddLeak()
Using siteCollection As New SPSite("http://moss")
Dim web As SPWeb = siteCollection.AllWebs.Add("site-relative URL")
' SPWeb object leaked.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
適切なコーディング方法
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.
}
Private Sub AllWebsAddNoLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.AllWebs.Add("site-relative URL")
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
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 innerWeb.
// innerWeb leak.
} // SPWeb object outerWeb.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub SPWebCollectionAddLeak(ByVal strWebUrl As String)
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
Dim webCollection As SPWebCollection = siteCollection.AllWebs ' No AllWebs leak just getting reference.
Dim innerWeb As SPWeb = webCollection.Add(strWebUrl) ' Must dispose innerWeb.
' innerWeb leak.
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
適切なコーディング方法
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.
}
Private Sub SPWebCollectionAddNoLeak(ByVal strWebUrl As String)
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
Dim webCollection As SPWebCollection = siteCollection.AllWebs ' No AllWebs leak just getting reference.
Using innerWeb As SPWeb = webCollection.Add(strWebUrl)
'...
End Using
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
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.
}
Private Sub AllWebsForEachLeak()
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
For Each innerWeb As SPWeb In siteCollection.AllWebs
' Explicitly dispose here to avoid out of memory leaks with large number of SPWeb objects.
Next
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
適切なコーディング方法 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.
}
Private Sub AllWebsForEachNoLeakOrMemoryOOM()
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
For Each innerWeb As SPWeb In siteCollection.AllWebs
Try
' ...
Finally
If innerWeb IsNot Nothing Then
innerWeb.Dispose()
End If
End Try
Next
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
適切なコーディング方法 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.
}
Private Sub AllWebsIndexerNoLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.AllWebs(0)
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
SPSite.OpenWeb メソッドおよび SPSite.SelfServiceCreateSite メソッド
SPSite オブジェクトの 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.
}
Private Sub OpenWebLeak()
Using web As SPWeb = New SPSite(SPContext.Current.Web.Url).OpenWeb()
' SPSite leaked !
End Using ' SPWeb object web.Dispose() automatically called.
End Sub
適切なコーディング方法
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.
}
Private Sub OpenWebNoLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
SPSite.RootWeb プロパティ
以前のガイダンスでは、SPSite.RootWeb プロパティは、使用元の SPSite オブジェクトを破棄する直前に呼び出し元のアプリケーションで破棄する必要があると説明していました。現在では、これは公式なガイダンスではありません。破棄のクリーンアップは、SharePoint Foundation および SharePoint Server によって自動処理されます。さらに、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 is obtained from SPContext.Current.Site.
}
Public Sub RootWebBestPractice()
' New SPSite.
Using siteCollection As New SPSite("http://moss")
Dim rootWeb1 As SPWeb = siteCollection.RootWeb
' No explicit rootWeb1 dispose required.
End Using ' siteCollection automatically disposed by implementing using().
' rootWeb1 will be Disposed by SPSite.
' SPContext and SPControl
Dim rootWeb2 As SPWeb = SPContext.Current.Site.RootWeb
' Also would apply to SPControl.GetContextSite(Context);
' No explicit rootWeb2 dispose required because it is obtained from SPContext.Current.Site.
End Sub
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.
}
}
Private Sub PersonalSiteLeak()
' Open a site collection.
Using siteCollection As New SPSite("http://moss")
Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
Dim personalSite As SPSite = profile.PersonalSite ' Will leak.
End Using
End Sub
適切なコーディング方法
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)
{
// ...
}
}
}
Private Sub PersonalSiteNoLeak()
' Open a site collection.
Using siteCollection As New SPSite("http://moss")
Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
Using personalSite As SPSite = profile.PersonalSite
' ...
End Using
End Using
End Sub
別のエッジ ケースでは、次のコード例に示すように、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.
}
}
Private Sub PersonalSiteLeak()
' Open a site collection.
Using siteCollection As New SPSite("http://moss")
Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
Dim personalSite As SPSite = profile.PersonalSite ' Will leak.
End Using
End Sub
この種のリークは、次のコード例に示すパターンに従うことで解決できます。
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)
{
// ...
}
}
}
Private Sub PersonalSiteNoLeak()
' Open a site collection
Using siteCollection As New SPSite("http://moss")
Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
Using personalSite As SPSite = profile.PersonalSite
' ...
End Using
End Using
End Sub
また、次のコード例に示すように、PersonalSite オブジェクトを ProfileLoader から取得すると、パフォーマンスの向上 (および SPSite オブジェクトの作成の回避) を行うことができます。
UserProfile myProfile = ProfileLoader.GetProfileLoader().GetUserProfile();
using (SPSite personalSite = myProfile.PersonalSite)
{
// ...
}
Dim myProfile As UserProfile = ProfileLoader.GetProfileLoader().GetUserProfile()
Using personalSite As SPSite = myProfile.PersonalSite
' ...
End Using
さらに、個人用サイトの Web パーツを作成する場合は、破棄が不要な PersonalSite のインスタンスを使用できます。
IPersonalPage currentMySitePage = this.Page as IPersonalPage;
if (currentMySitePage != null && !currentMySitePage.IsProfileError)
{
SPSite personalSite = currentMySitePage.PersonalSite; // Will not leak.
// ...
}
Dim currentMySitePage As IPersonalPage = TryCast(Me.Page, IPersonalPage)
If currentMySitePage IsNot Nothing AndAlso (Not currentMySitePage.IsProfileError) Then
Dim personalSite As SPSite = currentMySitePage.PersonalSite ' Will not leak.
' ...
End If
SPWeb オブジェクト
このセクションでは、返される SPWeb オブジェクトの破棄が必要な場合について説明します。
SPWeb.ParentWeb プロパティ
更新されたガイダンス
以前のガイダンスでは、呼び出し元のアプリケーションが SPWeb.ParentWeb を破棄するよう推奨していました。現在ではこれは公式なガイダンスではありません。破棄のクリーンアップは SharePoint Foundation および SharePoint Server によって自動処理されます。
注意
このベスト プラクティスでは、SharePoint Dispose Checker Tool (英語) で SPDisposeCheckID_170 として識別される問題に対処できます。
適切なコーディング方法
using (SPSite site = new SPSite("https://localhost"))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists["Announcements"];
SPWeb parentWeb = list.ParentWeb; //No explicit dispose required.
}
}
Using site As New SPSite("https://localhost")
Using web As SPWeb = site.OpenWeb()
Dim list As SPList = web.Lists("Announcements")
Dim parentWeb As SPWeb = list.ParentWeb 'No explicit dispose required.
End Using
End Using
SPWeb.Webs プロパティ
ここでは、Webs プロパティ コレクションのメソッド、プロパティ、および演算子のうち、アクセス後に SPWeb オブジェクトの破棄が必要なものについて説明します。
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.
}
Private Sub WebsLeak()
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
For Each innerWeb As SPWeb In outerWeb.Webs
' SPWeb innerWeb leak.
Next
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
適切なコーディング方法
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.
}
Private Sub WebsNoLeak()
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
For Each innerWeb As SPWeb In outerWeb.Webs
Try ' Should be first statement after foreach.
' ...
Finally
If innerWeb IsNot Nothing Then
innerWeb.Dispose()
End If
End Try
Next
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
SPWeb.Webs.Add
SPWeb.Webs.Add メソッド (または SPWebCollection.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.
}
Private Sub WebsAddLeak(ByVal strWebUrl As String)
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Dim addedWeb As SPWeb = web.Webs.Add(strWebUrl) ' Will leak.
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
適切なコーディング方法
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.
}
Private Sub WebsAddNoLeak(ByVal strWebUrl As String)
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Using addedWeb As SPWeb = web.Webs.Add(strWebUrl)
'..
End Using
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
SPWeb.Webs[] インデックス演算子
SPWeb.Webs[] インデックス演算子は、アクセスのたびに新しい SPWeb オブジェクトを返します。OpenWeb メソッドを呼び出したときに、そのオブジェクトが既にアクセス済みの場合でも SPWeb は作成されます。次のコード例は, .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);
}
Dim i As Integer
Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)
oSPWeb = oSPSite.OpenWeb()
For i = 0 To oSPWeb.Webs.Count - 1
oSPWeb2 = oSPWeb.Webs(i)
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
Next i
不適切なコーディング方法 2
foreach ループの使用
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPWeb = oSPSite.OpenWeb();
foreach(SPWeb oSPWeb2 in oSPWebe.Webs)
{
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}
Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)
oSPWeb = oSPSite.OpenWeb()
For Each oSPWeb2 As SPWeb In oSPWebe.Webs
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
Next
修正には、各ループの末尾で破棄する方法を推奨します。
適切なコーディング方法 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();
Dim i As Integer
Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)
oSPWeb = oSPSite.OpenWeb()
For i = 0 To oSPWeb.Webs.Count - 1
oSPWeb2 = oSPWeb.Webs(i)
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
oSPWeb2.Dispose()
Next i
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();
Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)
oSPWeb = oSPSite.OpenWeb()
For Each oSPWeb2 As SPWeb In oSPWeb.Webs
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
oSPWeb2.Dispose()
Next
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);
}
}
}
Dim i As Integer
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)
Using oSPWeb As SPWeb = oSPSite.OpenWeb()
For i = 0 To oSPWeb.Webs.Count - 1
Using oSPWeb2 As SPWeb = oSPWeb.Webs(i)
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
End Using
Next
End Using
破棄が必要なその他のオブジェクト
ここでは、その他の SharePoint オブジェクトに対して Dispose メソッドを呼び出すことが必要な状況について説明します。
Microsoft.SharePoint.Portal.SiteData.Area.Web プロパティ
SharePoint.Portal.SiteData.Area クラスの Web プロパティは、アクセスのたびに新しい SPWeb オブジェクトを返します。Area.Web プロパティを使用するときには、対応する Dispose メソッドの呼び出しが必要です。Area クラスと AreaManager クラスは現在では不使用ですが、レガシ コードを移行するときには、引き続き注意が必要です。
注意
このベスト プラクティスでは、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.
}
Private Sub AreaWebLeak()
' AreaManager and Area are obsolete in SharePoint Server, but this
' should still be noted.
Dim area As Area = AreaManager.GetArea(PortalContext.Current, New Guid("{GUID}"))
Dim str As String = area.Web.Title
' SPWeb area.Web leak.
End Sub
適切なコーディング方法
public void AreaWebNoLeak()
{
// AreaManager and Area are obsolete but this should still be noted.
Area area = AreaManager.GetArea(PortalContext.Current, new Guid("{GUID}"));
using (SPWeb areaWeb = area.Web)
{
string str = areaWeb.Title;
}
}
Public Sub AreaWebNoLeak()
' AreaManager and Area are obsolete but this should still be noted.
Dim area As Area = AreaManager.GetArea(PortalContext.Current, New Guid("{GUID}"))
Using areaWeb As SPWeb = area.Web
Dim str As String = areaWeb.Title
End Using
End Sub
SPControl.GetContextSite メソッドおよび SPControl.GetContextWeb メソッド
SharePoint コンテキスト オブジェクト (SPControl クラスの 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.
}
Private Sub SPControlBADPractice()
Dim siteCollection As SPSite = SPControl.GetContextSite(Context)
siteCollection.Dispose() ' DO NOT DO THIS.
Dim web As SPWeb = SPControl.GetContextWeb(Context)
web.Dispose() ' DO NOT DO THIS.
End Sub
適切なコーディング方法
void SPControlBestPractice()
{
SPSite siteCollection = SPControl.GetContextSite(Context);
SPWeb web = SPControl.GetContextWeb(Context);
// Do NOT call Dispose().
}
Private Sub SPControlBestPractice()
Dim siteCollection As SPSite = SPControl.GetContextSite(Context)
Dim web As SPWeb = SPControl.GetContextWeb(Context)
' Do NOT call Dispose().
End Sub
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.
}
Private Sub SPLimitedWebPartManagerLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Dim page As SPFile = web.GetFile("Source_Folder_Name/Source_Page")
Dim webPartManager As SPLimitedWebPartManager = page.GetLimitedWebPartManager(PersonalizationScope.Shared)
' SPWeb object webPartManager.Web leaked.
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
適切なコーディング方法
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.
}
Private Sub SPLimitedWebPartManagerLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Dim page As SPFile = web.GetFile("Source_Folder_Name/Source_Page")
Dim webPartManager As SPLimitedWebPartManager = page.GetLimitedWebPartManager(PersonalizationScope.Shared)
webPartManaber.Web.Dispose()
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Microsoft.SharePoint.Publishing.PublishingWeb
注意
Microsoft.SharePoint.Publishing 名前空間は SharePoint Server 2010 に属します。このセクションの内容は、SharePoint Server 2010 には該当しますが、SharePoint Foundation 2010 には該当しません。
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.
}
Private Sub PublishingWebCollectionLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
' Passing in SPWeb object that you own, no dispose needed on
' outerPubWeb.
Dim outerPubWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web)
Dim pubWebCollection As PublishingWebCollection = outerPubWeb.GetPublishingWebs()
For Each innerPubWeb As PublishingWeb In pubWebCollection
' innerPubWeb leak.
Next
' PublishingWeb will leak for each innerPubWeb referenced
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
適切なコーディング方法
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.
}
Private Sub PublishingWebCollectionNoLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
' Passing in SPWeb object that you own, no dispose needed on
' outerPubWeb.
Dim outerPubWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web)
Dim pubWebCollection As PublishingWebCollection = outerPubWeb.GetPublishingWebs()
For Each innerPubWeb As PublishingWeb In pubWebCollection
Try
' ...
Finally
If innerPubWeb IsNot Nothing Then
innerPubWeb.Close()
End If
End Try
Next
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
注意
Microsoft.SharePoint.Publishing.PublishingWeb.GetPublishingWebs が返す PublishingWebCollection に対し Add メソッドを呼び出して作成した PublishingWeb オブジェクトについても、同じようにそれぞれ Close を呼び出す必要があります。コード例については、GetPublishingWebs() メソッドを参照してください。このベスト プラクティスでは、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.
}
Private Sub GetVariationLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Dim publishingWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web) ' Passing in SPWeb object, so no Close() needed
Dim variationLabel As VariationLabel = Variations.Current.UserAccessibleLabels(0)
Dim variationPublishingWeb As PublishingWeb = publishingWeb.GetVariation(variationLabel) ' Must be Closed().
' ...
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
適切なコーディング方法
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.
}
Private Sub GetVariationNoLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Dim variationPublishingWeb As PublishingWeb = Nothing
Try
Dim publishingWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web) ' Passing in SPWeb object, so no Close() needed.
Dim variationLabel As VariationLabel = Variations.Current.UserAccessibleLabels(0)
variationPublishingWeb = publishingWeb.GetVariation(variationLabel) ' Must be Closed().
' ...
Finally
If variationPublishingWeb IsNot Nothing Then
variationPublishingWeb.Close()
End If
End Try
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
メソッド間にまたがった破棄のパターン
次のコードは、同じクラス内の複数のメソッド間で 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;
}
}
}
Public Class CrossMethodLeak
Private _siteCollection As SPSite = Nothing
Private _web As SPWeb = Nothing
Public Sub MethodA()
_siteCollection = New SPSite("http://moss")
_web = _siteCollection.OpenWeb()
End Sub
Public Sub MethodB()
If _web IsNot Nothing Then
Dim title As String = _web.Title
End If
End Sub
Public Sub MethodC()
If _web IsNot Nothing Then
Dim name As String = _web.Name
End If
End Sub
End Class
まとめ
IDisposable インターフェイスを実装した SharePoint オブジェクトは多くあるため、これらのオブジェクトをメモリに残してしまうことがないように注意が必要です。この記事で示したように、SharePoint オブジェクトを破棄する方法のガイドラインに従うことで、カスタム コードの信頼性が向上します。
謝辞
この記事の作成に協力いただいた以下の方々に感謝します。
Steve Sheppard (Microsoft Corporation)
Chris Gideon (Microsoft Corporation)
Rashid Aga (Microsoft Corporation)
関連項目
その他の技術情報
SharePoint Dispose Checker Tool (英語)
Patterns & Practices SharePoint Guidance (英語)
SharePoint Server 2007 デベロッパー ポータル
SharePoint 製品とテクノロジのカスタマイズの成功事例
Best Practices Resource Center for SharePoint Server 2007 (英語)