jump to navigation

SharePoint Object Model Memory Leaks September 26, 2006

Posted by codinglifestyle in SharePoint.
Tags: , , ,
add a comment

The SharePoint object model is the best way to develop complex solutions built upon a powerful architecture.  It has many faults, inconsistencies (Portal vs. SPS), and security can be a bear to overcome.  But never once did I think it leaked memory.  Forget that you are developing in C# for a moment, because from now on you’re going to have to start cleaning up after yourself.  That’s right, we’re going to program like its 1999:

"Several of the Windows SharePoint Services objects, primarily the SPSite class and SPWeb class objects, are created as managed objects. However, these objects use unmanaged code and memory to perform the majority of their work. The managed part of the object is small; the unmanaged part of the object is much larger. Because the smaller managed part of the object does not put memory pressure on the garbage collector, the garbage collector does not release the object from memory in a timely manner. The object’s use of a large amount of unmanaged memory can cause some of the unusual behaviors described earlier. Calling applications that work with IDisposable objects in Windows SharePoint Services must dispose of the objects when the applications finish using them. You should not rely on the garbage collector to release them from memory automatically."

Technically this is not a memory leak as in forgetting to delete an object in C++.  However, when developing web parts IIS is likely to grow large as the webpart is used causing the application pool to recycle quickly.  (This can also cause a problem in utilities or services which will grow large).  In our last install the application pool, which recycles when IIS reaches around 800megs, recycled every hour losing valuable cached data.  This meant the pain of caching that data again was felt every hour and was enough to force our investigation.  Please read the following article carefully to realize the full extent of this issue.  

http://msdn2.microsoft.com/en-us/library/ms778813.aspx

If you are a seasoned SharePoint developer I’m sure you won’t be surprised when it tell you this: its worse that you think.  You will also need to destroy (Dispose) the following:

·        All SPWeb/SPSite objects not obtained via GetContextWeb or GetContextSite

·        SPWeb/SPSite objects used from indexers or Arrays

·        Even SPWeb.RootWeb and SPWeb.ParentWeb must be destroyed if you touch it!  

Note on the first point, the single object obtained from GetContextWeb/Site is fine.  But any object created from the resulting SPWeb/SPSite must be disposed of.

 

Advertisements

Sharepoint SPAlert and UnAuthorizedAccessException June 2, 2005

Posted by codinglifestyle in Security, SharePoint.
Tags: , , , ,
add a comment

Sharepoint Portal 2003 has a fairly decent object model to work with.  Once you overcome the various security/permission issues it is quite powerful.  In fact this is the main issue in working with Sharepoint is the rite of passage for Sharepoint developers.  You will see little help and/or pity from other developers on forums when newbies hit these obstacles because most of the information is there in the SDK to be sorted out if you take the time.  However, there comes a time when your understanding is shaken when encountering a new and interesting error.  That time was last week for me when writing a metrics program which gathered statistics portal wide.  Everything was going smoothly, heck I was almost done in 2 days, when I hit a problem with SPAlerts which threw me for 2 days. 

Starting with a SPVirtualServer I iterated through the each SPSite and thus the SPSite.AllWebs.  The first SPWebCollection is for the portal itself (the following are personal and team SPS sites).  When foreaching (and new word?  we’ll see if it catches on) the SPWebs I was attempting to access to Alerts property to gather my stats.  This is where I ran in to trouble and started catching UnAuthorizedAccessException.  Below is the example from the SDK which is really there to just baffle you further because you think if they bother to show an example and I can’t even get that to work SPAlertCollection class has an example I can’t even get to work:

SPSite siteCollection = SPControl.GetContextSite(Context);
SPWebCollection sites = siteCollection.AllWebs;

foreach (SPWeb site in sites)
{
   SPAlertCollection alerts = site.Alerts;

   foreach (SPAlert alert in alerts)
   {
      Label1.Text += SPEncode.HtmlEncode(site.Title) + " :: " + SPEncode.HtmlEncode(alert.Title) + " :: " +
          alert.User.LoginName + "
";
   }
}

The above code is so simple that to have obtained the exception I was certain that it was a simple permissions problem rather than a coding problem.  The siteCollection.CurrentUser was the COMPUTER\Administrator. That user us in the Administrators group. That user was setup as a Sharepoint administrator and as the DB admin. SiteCollection.CurrentUser.IsSiteAdmin == true.

Context.User.Identity.Name
@”VM2003S1\Administrator”
Context.User.Identity.AuthenticationType
“NTLM”
Context.User.Identity.IsAuthenticated
true
Context.User.IsInRole(@”BUILTIN\Administrators”)
true
////////
// First site (the root) from SPSite.AllWebs
////////
site.Url
http://miintranet”
site.CurrentUser.LoginName
@”VM2003S1\administrator”
site.CurrentUser.IsSiteAdmin
true
site.AuthenticationMode
Windows
site.IsRootWeb
true
site.CurrentUser.Roles.Count
1
site.CurrentUser.Roles[0].Name
“Administrator”
site.Roles[“Administrator”].Users.Count
3
site.Roles[“Administrator”].Users[2].Name
@”VM2003S1\Administrator”
site.UserIsSiteAdmin
true
site.Alerts.Count
0
/////////
// Iterate to next site in SPSite.AllWebs
/////////
site.Url
http://miintranet/C0″
site.CurrentUser.LoginName
@”VM2003S1\administrator”
site.CurrentUser.IsSiteAdmin
true
site.UserIsSiteAdmin
true
site.AuthenticationMode
Windows
site.IsRootWeb
false
site.CurrentUser.Roles.Count
>UnAuthorizedAccessException
site.Roles.Count
5
site.Roles[“Administrator”].Users.Count
>UnAuthorizedAccessException
site.Alerts.Count
>UnAuthorizedAccessException

In the end it wasn’t a permission problem at all.  It seems what I was hitting is a SharePoint Services vs Sharepoint Portal issue. I may be too new at this to make an accurate assessment, but it seems that the Portal SDK lets us down by not clearly differentiating which technology they are talking about. And furthermore Microsoft let us down by having these technologies side-by-side yet not making them interoperable. This is compounded by the fact that from the Portal context I can access a SPSite.AllWebs[x].Alerts and there seems to be nothing wrong with this on the surface. The only inkling that we’re crossing boundaries is the namespace which I will be much more paranoid of in the future. Note that my “workaround” accessed the Alert (NOT SPAlert) class from the user profile manager. This Alert class is in the Microsoft.SharePoint.Portal.Alerts.Alert namespace. While the example which confounded and frustrated me for 2 days dealt with SPAlerts in the Microsoft.SharePoint.SPAlert namespace. So coder beware the exceptions you see may have more to do with crossing the technology border from Portal->Services or vice-versa. One can only hope that in the future MS will do a better job documenting or even better, making a cohesive object model which doesn’t lead you down these nebulous dead-ends.

The workaround I eventually used was to use to the UserProfileManager.  You will note that this gets you an Alert from the Portal namespace and thus no access issues.

 

 

//Get the root URL and the PortalContext.
string url = GetRootUrl();
SPSite rootSite = SPControl.GetContextSite(Context);
rootSite.AllowUnsafeUpdates=true;
PortalContext portalContext = null;
Uri uri = new Uri(url); 

TopologyManager topologyManager = new TopologyManager();
PortalSiteCollection sites = topologyManager.PortalSites;
portalContext = PortalApplication.GetContext(sites[uri]); 

//Get all the users that have access.
SPUserCollection allUsers = rootSite.RootWeb.AllUsers;
UserProfileManager upm = new UserProfileManager(portalContext); 

foreach (SPUser user in allUsers)
{
	//Get the user profile for the SPUser (from SPUserCollection)
	UserProfile up;
	try
	{
		up = upm.GetUserProfile(user.LoginName);
	}
	catch
	{
		continue;
	}
	int n = up.Alerts.Count;
}