Managing the Heap in Salesforce.com

August 16th, 2010

With the Spring ’10 release, Salesforce.com removed the limit on the number of items a collection can hold. So now, instead of ensuring that your collections contain no more than 1000 items, you have to monitor your heap size. Here are some strategies on how to write Apex scripts that run within these limits.

First of all, what is the heap? Dynamic memory allocation (also known as heap-based memory allocation) is the allocation of memory storage for use in a computer program during the runtime of that program. In Apex the heap is the reference to the amount of memory used by your reachable objects for a given script and request. When you create objects in your Apex code, memory is allocated to store these objects.

As with many other things in Force.com, there are governors and limits that prevent you from hijacking the heap and degrading the performance of other running applications. The heap limit is calculated at runtime and differs on how your code is invoked:

  • Triggers – 300,000 bytes
  • Anonymous Blocks, Visualforce Controllers, or WSDL Methods – 3,000,000 bytes
  • Tests – 1,500,000 bytes

These limits also scale with trigger batch sizes:

  • For 1-40 records, the normal limits apply
  • For 41-80 records, two times the normal limits apply
  • For 81-120 records, three times the normal limits apply
  • For 121-160 records, four times the normal limits apply
  • For 161 or more records, five times the normal limits apply

Luckily Salesforce.com increased the heap size limits in Summer ’10 but you still may run into some issues. Here are a few things you can do to write heap-friendly code.

Watch the Heap

When your scripts run you can view the heap size in the debug logs. If you notice your heap approaching the limit, you will need to investigate why and try to refactor your code accordingly.

Use the Transient Keyword

Try using the “Transient” keyword with variables in your controllers and extensions. The transient keyword is used to declare instance variables that cannot be saved, and shouldn’t be transmitted as part of the view state for a Visualforce page.

Use Limit Methods

Use heap limits methods in your Apex code to monitor/manage the heap during execution.

  • Limits.getHeapSize() – Returns the approximate amount of memory (in bytes) that has been used for the heap in the current context.
  • Limits.getLimitHeapSize() – Returns the total amount of memory (in bytes) that can be used for the heap in the current context.

// check the heap size at runtime
if (Limits.getHeapSize > 275000) {
     // implement logic to reduce
}

One strategy to reduce heap size during runtime is to remove items from the collection as you iterate over it.

Put Your Objects on a Diet

If the objects in your collection contain related objects (i.e., Account objects with a number of related Contacts for each) make sure the related objects only contain the fields that are actually needed by your script.

Use SOQL For Loops

SOQL “for” loops differ from standard SOQL statements because of the method they use to retrieve sObjects. To avoid heap size limits, developers should always use a SOQL “for” loop to process query results that return many records. SOQL “for” loops retrieve all sObjects in a query and process multiple batches of records through the use of internal calls to query and queryMore.

1
2
3
4
5
6
7
8
for (List<Contact> contacts : [select id, firstname, lastname 
  from contact where billingState = 'NY']) {
 
  // implement your code to modify records in this batch
 
  // commit this batch of 200 records
  update contacts;
}

Note: There used to be a strategy for dealing with heap intensive scripts by moving them asynchronous, @Future methods. However, since the heap limits were increased in Summer ’10 this is no longer a reason to simply use @Future method as the limits are the same.

Any other ideas on how to effectively manage the heap?

Categories: Apex, Salesforce

Leave a comment

Comments Feed8 Comments

  1. Jason

    “One strategy to reduce heap size during runtime is to remove items from the collection as you iterate over it.”

    Depends on the type of loop you are using. This will work:
    List letters = new List{‘a’,'b’,'c’};

    for(Integer i = 0; i < letters.size() + 1; i++){
    letters.remove(0);
    }

    But this will thrown and exception, 'List cannon be modified during iteration":
    List letters = new List{‘a’,'b’,'c’};
    Integer count = 0;

    for(String s : letters){
    letters.remove(count);
    count++;
    }

  2. Jason

    First like above should be:

    for(Integer i = 0; i < letters.size(); i++){

  3. Mohammad Swaleh

    Once again nice post Jeff..
    Just to add what Jason mentioned, we can “clear” the lists that we are done using and we are going forward doing some more stuff in our class or trigger.
    myList.clear();

  4. Joel Dietz / d3developer

    Won’t variables marked transient still have memory allocated for them? Or does the viewstate count double for the heap (once for the variable itself, once as it is included in the viewstate) ?

  5. osama

    If we mark the variables as transient, cant we use them on multiple pages with same controller?

    To avoid “‘List cannon be modified during iteration”, I create a new collection on runtime from the old one and then remove the old collection.

  6. vijay

    Hi Jeff,
    Can u help me please

    my code is given below

    if(salesOrderList.size()>0){
    System.debug(‘salesOrderList.size(): ‘ + salesOrderList.size());
    integer i = 0;
    List lst = new List();
    for(Sales_Order__c S : salesOrderList){
    integer d1 = i/2;System.Debug(‘sanjeeb: d1: ‘ + d1);
    integer res = i – (2*d1);System.Debug(‘sanjeeb: res: ‘ + res);
    string color = ”; Error Line Apex heap size too large: 3000621
    if(res == 0){
    color = ‘#D2EFF8′;
    }else{
    color = ‘#1797CO’;
    }
    if(S.Sales_Order_Line_Items__r.size() > 0)
    i++;
    for(Sales_Order_Line_Item__c LI : S.Sales_Order_Line_Items__r){
    WrapperClass WC = new WrapperClass(S, LI);
    WC.BGColor = Color;
    WC.ForeColor = ‘Black’;
    lstW.add(WC);
    }

    }
    }

    Here I am not creating a collection as per my assumtion. I am setting this value as blank each time in loop still getting error. Is transient is needed here while it is a local variable.

    Thank
    Vijay

  7. vijay

    sorry Jeff.. Error line is
    integer res = i – (2*d1);

  8. vijay

    Hey Jeff I have solve my issue …
    There is no issue in given code

    Thanks

Leave a comment

Feed

http://blog.jeffdouglas.com / Managing the Heap in Salesforce.com

WordPress Appliance - Powered by TurnKey Linux