Disposing of the Report’s Memory
Web applications that produce a high volume of reports frequently have a problem with running out of memory. This is because reports require a lot of memory to process them as well as store the actual report object in memory. Properly disposing of a report’s memory footprint on the server differs from how you dispose of other objects and it isn’t very well understood by most programmers. This makes it easy to think that your reports are being removed from memory when in reality they aren’t. Let’s look at what is involved in properly disposing of reports from memory.
When you print a report, there are three ways that it uses resources on the server. As you saw in the previous section, the ReportDocument object is used to load a report onto the page, and it obviously consumes memory. In addition to that, the actual report as it is rendered requires memory.Lastly, the Session collection has a copy of the report in its memory. You have to dispose of the memory for each of these circumstances to completely optimize the web server’s resources.
When a web page closes, the ReportDocument object variable falls out of scope. .NET keeps it in memory until it determines that it is necessary to perform garbage collection for the entire application. I’ve found in certain high-volume sites that garbage collection isn’t as reliable as I would like it to be and I have to force garbage collection manually. In this circumstance, I’ll call the Dispose() method of the report object to drop it out of scope, and then call the GC.Collect() method to force garbage collection.
The physical copy of the report is removed from memory by calling the Close() method of the ReportDocument object. I put this immediately before calling the Dispose() method. If you dispose of the report object before closing the report, it loses the internal link to the physical report and you won’t be able to call the Close() method on it. It is essential that you do this in the proper order.
I put my cleanup code in the Page_Unload() event. This is the last event called when the web page finishes processing the page data and passes the HTML code to the browser. Many people are confused by the Page_Unload() event because they think that it is called when the user closes the page on the browser. But this isn’t the case. Since the browser is stateless and doesn’t have a direct connection back to the web server, there is no way to determine when the user has closed the browser window. Thus, the Page_Unload() event can’t be called when the web page closes because the web server won’t know when this happens. What really happens is that when the server has finished generating the entire page output and is ready to send the HTML code to the browser, it calls the Page_Unload() event. Since the HTML has been created, the web server no longer needs any of the data that was used to build the page. You can put code in the Page_Unload() event to clean up any orphaned objects that were created during the processing of the page (data connections, reports, etc.) Listing 14-7 is the sample code that closes the report and disposes of its memory.
Listing 14-7. Removing a report from an ASP.NET page.
Private Sub Page_Unload(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Unload
MyReport.Close()
MyReport.Dispose()
GC.Collect()
End Sub
private void Page_Unload(object sender, System.EventArgs e) Handles MyBase.Unload
{
MyReport.Close();
MyReport.Dispose();
GC.Collect();
}
The third place where you need to clean up the report’s memory is in the Session collection. Since the report is stored there between page loads, it also needs to be removed when the user is finished viewing the report. This is done automatically by the web server because, at some point, the user’s session will time out and the objects in the Session collection get cleared. This works for the majority of web sites, but it won’t be good enough for a high volume site that generates many reports. For this scenario, you have to remove the report from the Session collection manually. The code is pretty simple and it is shown in Listing 14-8.
Listing 14-8. Removing the report from the Session collection.
Session("myReport")=Nothing
Session["myReport"] = null;
The next decision you have to make is where to put this code. We know that the report needs to be cleared from the Session collection when the web page is closed, but we learned earlier that it isn’t possible to know when the user closes the page. So how do you know when to remove the report from the Session? You don’t. What I do is put the code to clear the report from the Session collection on the main menu page for the web application. I know that the main menu is the page most frequently visited by every user. By putting the code there, I ensure that it will be removed in a relatively short period of time. Of course, this rule of thumb doesn’t apply to every web application and you have to use your best judgment when determining where to put your code.
You might also notice that in this code I referred to the report name as “myReport”. This brings up another potential problem because if your web application has many reports on it, the main menu page would need a lot of code to clear out all the potential reports that the application can generate. Every page that produces a report needs to have that report removed from the Session collection. To fix this problem, I use the same session name for all the reports in the web application. Since the web application can only display one report at a time, it’s okay to re-use the same name without worrying about conflicts. In addition to that, since the report is cleared out on the main menu page, I know that it will be empty when the next page with a report loads.