Blog

All Blog Posts

Always dispose resources! {or: Always clean up after yourself!}

Always Dispose Resources

A few years ago, my team was assigned to work on a legacy Crystal Reports project that had been in production for over five years. While making changes, we decided it was a good time to upgrade it to the latest version of Crystal Reports.

After about a week of work and extensive functional testing, the team agreed we were good to go. On the night of the install, we ran a quick smoke test. Everything worked fine so we called the job done.

Unfortunately, the following morning we started to get notifications of the application crashing, and soon enough, unhappy clients started to call. Upon further investigation, we observed the reports application would run fine for 15 to 30 minutes before experiencing problems.

Further monitoring of the server showed the amount of RAM usage increasing over time to the point the server was continuously swapping memory to disk. It would progressively become slower and more unstable, eventually requiring a reboot.

Looking more closely into the code, we realized the original developers never thought about disposing the reports. As such, RAM was allocated upon generating and serving a report, but never freed. Every single report would stay in RAM until the server was rebooted or the application pool restarted.

Although not disposing a resource is a bad practice from a software engineering standpoint, everything worked fine for many years. At the time the application was originally deployed, there were few clients using the application, and the server was rebooted every night. But over time the client base grew, and newer and more resource-intensive versions of Crystal Reports were deployed. Our last upgrade was the last straw.

To preserve the existing error handling mechanism but dispose the resource, we implemented a Dispose Pattern like this:

Resource resource = null;

        public void test() {

            try

            {

                // Attempt to acquire the resource.

                resource = getResource();

                // Perform actions with the resource.

                ...

            }

            finally

            {

                // Resource might not have been acquired, or was already freed

                if (resource != null)

                    resource.dispose();

            }

        }

The final statement is always executed, regardless of whether or not an exception occurred, thus ensuring the resource is always disposed. Notice there is no catch statement here. We wanted any exception to “trickle up” to the global.asax file, where it is properly handled (logged, displayed as a nice error message, etc.).

In conclusion, that day we learned two important practices the hard way that I hope will help you avoid the same fate:

  1. Always dispose resources after using them.
  2. Whenever it is possible (and makes sense), along with functional testing, run some performance and/or load stress tests before deploying to production.

Related posts