5

while playing with custom claims provider I've found interesting thing in SPActiveDirectoryClaimProvider. If you investigate a code using any .net assembly browser\decompiler (.NET Reflector or ILSpy) you will find a memory leak of SPWeb object in method FillResolve. Take a closer look at the code:

// Microsoft.SharePoint.Administration.Claims.SPActiveDirectoryClaimProvider
private void FillResolve(Uri context, string[] entityTypes, bool inputIsEmailOnly, string resolveInput, List<PickerEntity> resolved)
{
    SPWeb sPWeb = null;
    SPSite sPSite = null;
    if (!SPThreadContext.Items.Contains("SPActiveDirectoryClaimProviderIgnoreContext"))
    {
        bool catchAccessDeniedException = SPSecurity.CatchAccessDeniedException;
        try
        {
            SPSecurity.CatchAccessDeniedException = false;
            sPSite = new SPSite(context.AbsoluteUri);
            if (sPSite != null)
            {
                sPWeb = sPSite.OpenWeb();
            }
            if (!sPWeb.Exists)
            {
                sPWeb = null;
            }
        }
        catch
        {
            if (sPSite != null)
            {
                sPSite.Dispose();
                sPWeb = null;
            }
        }
        finally
        {
            SPSecurity.CatchAccessDeniedException = catchAccessDeniedException;
        }
    }
    try
    {
        SPWebApplication webApp = SPWebApplication.Lookup(context);
        SPPrincipalType scopes = SPActiveDirectoryClaimProvider.ConvertEntityTypeToPrincipalTypes(entityTypes);
        SPPrincipalInfo sPPrincipalInfo = SPUtility.ResolveWindowsPrincipal(sPWeb, webApp, resolveInput, scopes, inputIsEmailOnly);
        if (sPPrincipalInfo != null)
        {
            if (inputIsEmailOnly && MatchType.EmailAddress == sPPrincipalInfo.MatchType && SPActiveDirectoryClaimProvider.DisableEmailResolve())
            {
                ULS.SendTraceTag(2466693u, ULSCat.msoulscat_WSS_ClaimsAuthentication, ULSTraceLevel.VerboseEx, "Dicarding match '{0}' for input '{1}' because it is email match and feature is disabled.", new object[]
                {
                    sPPrincipalInfo.LoginName,
                    resolveInput
                });
            }
            else
            {
                PickerEntity pickerEntity = this.CreatePickerEntity(sPPrincipalInfo);
                if (pickerEntity != null)
                {
                    resolved.Add(pickerEntity);
                    ULS.SendTraceTag(1634481712u, ULSCat.msoulscat_WSS_ClaimsAuthentication, ULSTraceLevel.VerboseEx, string.Format(CultureInfo.InvariantCulture, "Returning AD resolve match '{0}' for input '{1}'.", new object[]
                    {
                        pickerEntity.Key,
                        resolveInput
                    }));
                }
            }
        }
        else
        {
            if (!inputIsEmailOnly)
            {
                int maxCount = 10;
                bool flag = false;
                IList<SPPrincipalInfo> list;
                if (sPWeb != null)
                {
                    list = SPUtility.SearchPrincipals(sPWeb, resolveInput, scopes, SPPrincipalSource.Windows, null, maxCount, out flag);
                }
                else
                {
                    list = SPUtility.SearchWindowsPrincipals(webApp, resolveInput, scopes, maxCount, out flag);
                }
                List<PickerEntity> list2 = new List<PickerEntity>();
                foreach (SPPrincipalInfo current in list)
                {
                    if (inputIsEmailOnly && MatchType.EmailAddress == current.MatchType && SPActiveDirectoryClaimProvider.DisableEmailResolve())
                    {
                        ULS.SendTraceTag(2466694u, ULSCat.msoulscat_WSS_ClaimsAuthentication, ULSTraceLevel.VerboseEx, "Dicarding match '{0}' for input '{1}' because it is email match and feature is disabled.", new object[]
                        {
                            current.LoginName,
                            resolveInput
                        });
                    }
                    else
                    {
                        PickerEntity pickerEntity2 = this.CreatePickerEntity(current);
                        if (pickerEntity2 != null)
                        {
                            list2.Add(pickerEntity2);
                            ULS.SendTraceTag(1634481713u, ULSCat.msoulscat_WSS_ClaimsAuthentication, ULSTraceLevel.VerboseEx, string.Format(CultureInfo.InvariantCulture, "Returning AD resolve search match '{0}' for input '{1}'.", new object[]
                            {
                                pickerEntity2.Key,
                                resolveInput
                            }));
                        }
                    }
                }
                if (list2.Count == 1 && list2[0].Claim != null && SPClaim.EqualClaimValues(list2[0].Claim.Value, resolveInput))
                {
                    resolved.Add(list2[0]);
                }
                else
                {
                    if (list2.Count > 0)
                    {
                        PickerEntity pickerEntity = base.CreatePickerEntity();
                        pickerEntity.IsResolved = false;
                        pickerEntity.DisplayText = resolveInput;
                        pickerEntity.MultipleMatches.AddRange(list2);
                        resolved.Add(pickerEntity);
                    }
                }
            }
        }
    }
    catch
    {
        throw;
    }
    finally
    {
        if (sPSite != null)
        {
            sPSite.Dispose();
        }
    }
} 

According to the best practice (http://msdn.microsoft.com/en-us/library/office/ee557362%28v=office.14%29.aspx) we should dispose SPWeb objects that were created using OpenWeb method. But sPWeb object is not disposed in any part of the method. It means, that every time we perform search and resolve in people picker, one SPWeb object will be leaked. I don't have msdn subscription in order to contact Microsoft regarding this problem. This issue is true for SharePoint 2010 (SP2) and SharePoint 2013 (SP1).

I believe community can confirm memory leak and contact Microsoft to fix this issue.

2

Calling Dispose() on the SPSite that the OpenWeb method was used on will automatically dispose of the SPWeb it opened. As long as the SPSite is being disposed of, you won't have issues here unless you are opening a ton of SPWeb's and not disposing them explicitly.

Looking at that code, it appears to be disposing of the SPSite, so it should be fine.

EDIT

For reference, here is the .Close() method from the SPSite class called by the .Dispose() method. As you can see, it disposed of any SPWebs it opened before disposing itself:

public void Close()
{
    SPEventManager.WaitForPostEvents();
    if (this.m_openedWebs != null)
    {
        SPWeakObjectHandleList<SPWeb> list = new SPWeakObjectHandleList<SPWeb>(this.m_openedWebs);
        foreach (SPWeb web in list)
        {
            web.Close();
        }
        list.Dispose();
        this.m_openedWebs.Dispose();
    }
    this.m_rootWebCreated = false;
    SPRequestContext.UnregisterSite(this);
    this.InvalidateSite();
}
| improve this answer | |
  • 1
    BTW there are a LOT of false-positive memory leaks reported by these tools on the SharePoint assemblies. – John Chapman May 19 '14 at 20:59
  • 1
    I completely disagree with you. When using spsite and spweb (that was created using OpenWeb) you should dispose both spweb and spsite. Take a look at the msdn best practice - msdn. They always dispose spweb and spsite in every sample regarding OpenWeb. Could you please provide msdn article or any proof link were described, that you can not dispose spweb, that was created using OpenWeb? – Sergei Sergeev May 19 '14 at 21:10
  • 2
    I agree, you should dispose the SPWebs, but you don't have to. Particularly if you are just opening one or two SPWebs. If you open Microsoft.SharePoint.dll in .NET Reflector, look at the .Close() method on the SPSite class (this is called by Dispose()). It disposes of any open SPWebs that it had opened before disposing. – John Chapman May 19 '14 at 21:14
  • Edited the answer to include the SPSite.Close() method. – John Chapman May 19 '14 at 21:16
  • 1
    HA! Yes, still good practice. There are a few bloggers out there who thing it's unnecessary (for SPSites and SPWebs), but tend to stay on the safer side and dispose everything. – John Chapman May 19 '14 at 21:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.