salesforce零基礎學習(一百二十一)Limitation篇之Heap Size Limitation salesforce 零基礎學習(四十二)簡單文件上傳下載

 本篇參考:

https://help.salesforce.com/s/articleView?id=000384468&type=1

https://help.salesforce.com/s/articleView?id=000385712&type=1

此前講過CPU limitation:salesforce零基礎學習(一百零二)Limitation篇之 CPU Limit
本篇說一下項目中也經常用到的 heap size limitation以及best practice.
首先先說一下 salesforce中的 heap size簡單概念, salesforce中的heap size和java中的heap size概念基本相同,當對象或者變量創建時,就會給分配內存,當運行時基於邏輯動態分配內存。salesforce限制同步最大的 apex heap size是6MB,異步的場景最多的是12MB. 當transaction執行時,太多數據存儲在內存中的情況下,可能觸發 The "Apex heap size too large" 的錯誤。

如果去調查某一個功能邏輯的heap size情況,可以通過以下的步驟來分析:

  • 通過debug log查看當前的 heap size情況。
  • 在debug log中通過HEAP_ALLOCATE來確定對象或者變量的分配內存的情況。
  • 通過最後的Maximum heap size: 瞭解當前的執行的transaction所使用的heap size情況。
  • 針對heap size limit擁有兩個方法可以查詢:
    1. Limits.getHeapSize():返回已用於堆的大致內存量(單位爲:字節)。
    2. Limits.getLimitHeapSize(): 返回堆中還可以使用的大致的內存量(單位爲:字節)

Best practice
1. 不使用class級別的變量去存儲大量數據(也不一定侷限於 class級別的變量,list儘量別存儲大量數據)
錯誤案例: 下面的demo中: baseList,SampleMap的value以及tempt list都指向了同一個內存地址,執行以後,這個內存地址便會超限,從而觸發The "Apex heap size too large" 的limitation

String tStr = 'aaaaa bbbbb ccccc ddddd eeeeee fffff ggggg 11111 22222 33333 44444';
List<String> baseList = tStr.split(' ');
List<String> bigList = baseList;
Map<integer, List<String>> SampleMap = new Map<integer, List<String>>();
SampleMap.put(1, bigList);

for (integer i=0; i<50; i++) {
    List<String> tempList = new List<String>();
    tempList = SampleMap.get(1);
    bigList.addAll(tempList);
}
system.debug('FINAL LIST SIZE IS '+bigList.size());

簡單的改動就是聲明一個新的 list,避免之前的內存倍速增長。

String tStr = 'aaaaa bbbbb ccccc ddddd eeeeee fffff ggggg 11111 22222 33333 44444';
List<String> baseList = tStr.split(' ');
Map<integer, List<String>> Sample = new Map<integer, List<String>>();
List<String> bigList = baseList;

Sample.put(1, bigList);
List<string> myList = new list<string>(); //Declare a new list

for (integer i=0; i<50; i++) {
    List<String> tempList = new List<String>();
    tempList = Sample.get(1);
    system.debug('templist: ' + tempList.size());
    system.debug(' bigList: ' + bigList.size());

    myList.addall(tempList); //original code is bigList.addall(tempList);
}

system.debug('FINAL LIST SIZE OF bigList IS '+ bigList.size());
system.debug('myList IS '+mylist.size());

2. 使用SOQL for loop從大量查詢的數據中迭代和處理數據,官方也介紹了很多的 SOQL for loop的demo。詳情可查看:

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_loops_for_SOQL.htm
未改進前的簡單的demo如下:

List<Account> accs = [SELECT Id, Name FROM Account LIMIT 50000];

針對 heap size修改的情況下,官方給出的建議,此種情況下,每200條執行一次數據,大量的減少了 heap size的使用。

SOQL for loop通過調用SOAP API的query和queryMore方法,使用高效的分塊來檢索所有sObjects(每次處理200條數據)。開發人員可以通過使用SOQL for loop處理返回多條記錄的查詢結果來避免堆大小的限制。

for(Account a : [SELECT Id, Name FROM Account LIMIT 50000]){
//TODO custom logic
}

當然,在多租戶環境下,我們的limitation也不止 heap size,上述方法是否是最優解需要具體情況具體分析。當我們使用 SOQL for loop並且數據量大的情況下,這種方法可能會導致使用更多的CPU週期,邏輯執行時間也變得多了。除了官方上面的鏈接介紹以外,也可以看一下下面的邱老闆的demo

https://blog.keal.us/salesforce/soql-for-loop%e7%9a%84%e6%95%88%e7%8e%87%e9%97%ae%e9%a2%98/
3.
變量使用 'transient'關鍵字,用於聲明不需要被保存的變量,並且在VF page情況下也不會計入view state

salesforce 零基礎學習(四十二)簡單文件上傳下載

4. 在運行時環境下,通過在迭代list / set / map時從集合中移除不必要的item來減小堆大小。

除此以外的幾點優化點作爲參考:
1. 避免使用無效的臨時變量。比如代碼中的臨時變量後續沒有調用,造成了額外的花銷,這種沒有用的代碼儘量刪除。
2. 更短的命名以及Field api 名稱: 誠然使用好的命名規範有更強的可讀性,不過短的名字確實可以省一些 heap size
移除不必要的debug log語句,特別是生產環境。

總結:heap size和CPU limitation的優化相輔相成,不要爲了某一個優化而特意放棄另外一個,彼此形成一下平衡。篇中有錯誤地方歡迎指出,有不懂歡迎留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章