Addressing Visualforce View State and Controller Heap Space Problems

Tuesday, May 31, 2011

Force.com makes it easy to write an MVC page with AJAX functionality and easy access to the data in your organization's Salesforce database. So easy, in fact, that the developer may feel TOO free from the shackles of normal implementation details and choose to go crazy with the data. Continuing on this vector, a practicing Salesforce developer will, without doubt, hit a governor limit on either the view state in the page or the heap size in controller. Be prepared for that day by learning about these two related things!

Let's start with the heap size error that you may hit: "Apex Heap Size is too Large". The 'heap' that is referenced is very similar the the less-abstract programming heap that you may have met in C++, Java, or other languages. When you declare a new variable in these other languages, memory is allocated for it in one of two places, either the heap or the stack. Similarly, your Apex controller is allocated memory space from the Force.com platform, and, because of hardware limitations and the multi-tenant architecture, you are only given a certain amount to use. When you request more than that amount of memory, SF gets angry and stops executing your code.

How do we solve this? Well, the biggest thing in your controller that is using heap space is your variables, so re-think your usage of them. Identify any variables or data structures that could be holding lots of data. Check out the Limits methods, and sprinkle a few System.debug('current heap usage: ' + Limits.getHeapSize()); statements around your code. The first place to look is at loops and any variables that are populated directly from a SOQL query. A simple, and inefficient, example:

Map<Id, Lead> notJohnsonLeadIndexMap = new Map<Id, Lead>([SELECT Account.Name, LastName FROM Lead WHERE LastName != 'Johnson']);
for (Lead notJohnsonLead : notJohnsonLeadIndexMap.values()) {
   if (notJohnsonLead.Account.Name == 'Acme') {
      leadToEmailList.add(notJohnsonLead);
   }
}

That SOQL query could bring in a huge flood of data, which we aren't even keeping. We could construct the query to better filter the returned results, or we can move the SOQL into the for definition to allow the compiler to optimize by using its QueryMore functionality and minimizing the amount of data stored on our controller:


for (Lead notJohnsonLead : [SELECT Account.Name, LastName FROM Lead WHERE LastName != 'Johnson']) {
   if (notJohnsonLead.Account.Name == 'Acme') {
      leadToEmailList.add(notJohnsonLead);
   }
}



Now, on to the view state in a VF page. It is a solution to a problem - the problem of how to synchronize the users's last page state between the client and your code in controller. HTTP is a stateless protocol by definition, which is part of what makes it so fast and efficient, but keeping state between the client and the server in this modern era is a necessity, so a work-around like the view state is necessary. What's the view state look like?

 You can see it in any Visualforce page that has form tags by right-click > View Source.


As of the Winter '11 release, SF provides a view state debugging tool that you can enable in your VF development mode footer. It's great for seeing what's taking up all that space in your view state, even breaking it down into percentages of total.

So, now you can see what's in your view state and what you should try to fix, but how do you do that? Well, any variables/data structures that you define in your controller are serialized into the view state and sent to the client along with the HTML code. Knowing that, you can again take extra care to ensure that all variables in your controller are being efficiently used and contains only the minimum required data. The transient keyword was created for pretty much this purpose, so try using it. By marking a variable as transient, it will be discarded when the controller has finished its job and the page is rendered. The downside is that this variable will have to be recreated each time the page loads, but that's a trade-off. For further reading, TehNrd's blog post on this topic is very informational and you have to check it out if you need more detailed explanation. Also, wiki.developerforce.com has a nice introduction to Visualforce view state.