Improving .NET Application Performance Part 9: Boxing and Unboxing

Improving .NET Application Performance Part 9: Boxing and UnboxingIn this next article in our series of creating optimized .NET code, we’ll discuss “boxing and unboxing”. In case you are unfamiliar with these terms, let’s take a brief look at what boxing and unboxing means.

You can convert value types to reference types and back again. When a value type variable needs to be converted to a reference type, an object (a box) is allocated on the managed heap to hold the value and its value is copied into the box. This process is known as boxing. Boxing can be implicit or explicit, as shown in the following code.

int p = 123;

Object box;

box = p;          // Implicit boxing

box = (Object)p;  // Explicit boxing with a cast

Boxing mostly occurs when passing a value type to a method that takes an object as parameter. When a value in an object is converted back into a value type, the value is copied out of the box and into the appropriate storage location. This process is known as unboxing.

p = (int)box; // Unboxing

Boxing issues are exacerbated in loops or when dealing with large amount of data such as large-sized collections storing value types.

Avoid Frequent Boxing and Unboxing Overhead

Boxing causes a heap allocation and a memory copy operation. To avoid boxing, do not treat value types as reference types. Avoid passing value types in method parameters that expect a reference type. Where boxing is unavoidable, to reduce the boxing overhead, box your variable once and keep an object reference to the boxed copy as long as needed, and then unbox it when you need a value type again.

int p = 123;

object box;

box = (object)p;  // Explicit boxing with a cast

//use the box variable instead of p


Collections and Boxing

Collections store only data with base type as Object. Passing value types such as integers and floating point numbers to collections causes boxing. A common scenario is populating collections with data containing int or float types returned from a database. This can cause excessive overhead in the case of collections due to iteration. Consider this snippet:

ArrayList al = new ArrayList();

for (int i=0; i<1000;i++)

  al.Add(i); //Implicitly boxed because Add() takes an object

int f = (int)al[0]; // The element is unboxed

To prevent this, consider using an array instead, or creating a custom collection class for your specific value type. You must perform unboxing with an explicit cast operator.

Measure Boxing Overhead

There are several ways to measure the impact of boxing operations. You can use Performance Monitor to measure the performance impact of boxing overhead on the resource utilization and response times for your application. To do a static analysis of where exactly you are affected by boxing and unboxing in your code, you can analyze MSIL code. Search for box and unbox instructions in MSIL by using the following command line.

Ildasm.exe yourcomponent.dll /text | findstr box

Ildasm.exe yourcomponent.dll /text | findstr unbox

However, you must watch out where exactly you optimize the boxing overhead. The overhead is significant in places where there are frequent iterations such as loops, inserting, and retrieving value types in collections. Instances where boxing occurs only once or twice are not worth optimizing.

Use DirectCast In Visual Basic .NET

Use the DirectCast operator to cast up and down an inheritance hierarchy instead of using CType. DirectCast offers superior performance because it compiles directly to MSIL. Also, note that DirectCast throws an InvalidCastException if there is no inheritance relationship between two types.

In our next article, we’ll discuss Exception Management.