In our previous article we discussed how to optimize threading in ASP.NET application. In this article we’ll look at resource management. Poor resource management from pages and controls is one of the primary causes of poor Web application performance. Poor resource management can place excessive loads on CPUs and can consume vast amounts of memory. When CPU or memory thresholds are exceeded, applications might be recycled or blocked until the load on the server is lower.
We’ll give some useful guidelines to help you manage your resources efficiently in the following sections.
ADO.NET provides built-in database connection pooling that is fully automatic and requires no specific coding. Make sure that you use the same connection string for every request to access the database.
Make sure you release pooled resources so that they can be returned to the pool as soon as possible. Do not cache pooled resources or make lengthy blocking calls while you own the pooled resource, because this means that other clients cannot use the resource in the meantime. Also, avoid holding objects across multiple requests.
Explicitly Call Dispose or Close on Resources You Open
If you use objects that implement the IDisposable interface, make sure you call the Dispose method of the object or the Close method if one is provided. Failing to call Close or Dispose prolongs the life of the object in memory long after the client stops using it. This defers the cleanup and can contribute to memory pressure. Database connection and files are examples of shared resources that should be explicitly closed. The finally clause of the try/finally block is a good place to ensure that the Close or Dispose method of the object is called. This technique is shown in the following Visual Basic® .NET code fragment.
If Not(conn Is Nothing) Then
In Visual C#®, you can wrap resources that should be disposed, by using a using block. When the using block completes, Dispose is called on the object listed in the brackets on the using statement. The following code fragment shows how you can wrap resources that should be disposed by using a using block.
SqlConnection conn = new SqlConnection(connString);
. . .
} // Dispose is automatically called on the connection object conn here.
Do Not Cache or Block on Pooled Resources
If your application uses resources that are pooled, release the resource back to the pool. Caching the pooled resources or making blocking calls from a pooled resource reduces the availability of the pooled resource for other users. Pooled resources include database connections, network connections, and Enterprise Services pooled objects.
Know Your Application Allocation Pattern
Poor memory allocation patterns may cause the garbage collector to spend most of its time collecting objects from Generation 2. Collecting objects from Generation 2 leads to poor application performance and high loads on the CPU.
Coding techniques that cause large numbers of temporary allocations during a short interval put pressure on the garbage collector. For example, when you perform a large number of string concatenation operations by using the += operator in a tight loop, or when you use String.Split for every request, you may put pressure on the garbage collector. All of these operations create hidden objects (temporary allocations). Use tools such as the CLR Profiler and System Monitor to better understand allocation patterns in your application.
Obtain Resources Late and Release Them Early
Open critical, limited, and shared resources just before you need them, and release them as soon as you can. Critical, limited, and shared resources include resources such as database connections, network connections, and transactions.
Avoid Per-Request Impersonation
Identify and, if necessary, authorize the caller at the Web server. Obtain access to system resources or application-wide resources by using the identity of the Web application process or by using a fixed service account. System resources are resources such as event logs. Application-wide resources are resources such as databases. Avoiding per-request impersonation minimizes security overhead and maximizes resource pooling.
Note that Impersonation on its own will not cause performance issues. However, impersonation often prevents efficient resource pooling. This is a common cause of performance and scalability problems.
In our next article we’ll talk about guidelines related to Pages.