<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>post Performance Best Practices for Coding Comma Separated Lists in ServiceNow AI Platform articles</title>
    <link>https://www.servicenow.com/community/servicenow-ai-platform-articles/performance-best-practices-for-coding-comma-separated-lists/ta-p/2398988</link>
    <description>&lt;DIV class="lia-message-template-content-zone"&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;&amp;lt; Previous Article&lt;/TD&gt;&lt;TD&gt;&amp;nbsp;&lt;/TD&gt;&lt;TD&gt;Next Article &amp;gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;A href="https://www.servicenow.com/community/now-platform-articles/caching-data-to-improve-performance/ta-p/2308096" target="_blank" rel="noopener"&gt;Caching data to improve performance&lt;/A&gt;&lt;/TD&gt;&lt;TD&gt;&amp;nbsp;&lt;/TD&gt;&lt;TD&gt;&lt;A href="https://www.servicenow.com/community/developer-articles/database-performance-improving-slow-or-and-join-queries/ta-p/2299441" target="_blank" rel="noopener"&gt;Database Performance: Improving Slow OR and JOIN Queries &lt;/A&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;HR /&gt;&lt;H2&gt;Overview&lt;/H2&gt;&lt;P&gt;This guide is written by the ServiceNow Technical Support Performance team (&lt;A href="https://community.servicenow.com/community?id=community_article&amp;amp;sys_id=0686dc99dbde241011762183ca9619e6" target="_blank" rel="noopener"&gt;All Articles&lt;/A&gt;). We are a global group of experts that help our customers with performance issues. If you have questions about the content of&amp;nbsp;&lt;EM&gt;this&amp;nbsp;article&lt;/EM&gt;&amp;nbsp;we will try to answer them here. However,&amp;nbsp;if you have urgent questions or specific issues, please see the list of resources on our profile page:&amp;nbsp;&lt;A href="https://community.servicenow.com/community?id=community_user_profile&amp;amp;user=e2299eed1b305490d01143f6fe4bcb4d" target="_blank" rel="noopener"&gt;ServiceNowPerformanceGTS&lt;/A&gt;.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;If you have developed in ServiceNow for a while, then you have probably come across the scenario where you want to code a comma separated list of items. For example, consider the following code:&lt;/P&gt;&lt;TABLE border="1" width="100%"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="100%"&gt;&lt;FONT color="#800000"&gt;WARNING: Do not use! There are two major problems with the below code!&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/DIV&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;function getThingsCreatedByUser(userId) {
  if (!userId) return null;
  var thingsGr = new GlideRecord("u_thing");
  thingsGr.addQuery("sys_created_by", userId);
  thingsGr.query();
  var result = "";
  while(thingsGr.next()) {
    result = result + thingsGr.sys_id + ",";
  }
  return (result.length) ? "sys_idIN" + result.substr(0,result.length-1) : null;
}&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Code like this might be called by a reference qualifier or a dynamic filter to get a list of things created by a certain user. However, this code has at least two&lt;FONT color="#FF0000"&gt;&lt;EM&gt; major weaknesses&lt;/EM&gt;&lt;/FONT&gt;. These could cause your whole ServiceNow node to run low on memory, causing severe performance degradation for all users who are logged in to that node:&lt;/P&gt;&lt;OL&gt;&lt;LI&gt;There is no limit on the number of items that can go in the comma separated list.&lt;/LI&gt;&lt;LI&gt;It uses String concatenation&lt;/LI&gt;&lt;/OL&gt;&lt;P&gt;In the rest of this article we will explain why the above weaknesses are dangerous and what you can do to avoid them through adopting certain coding best practices.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;1. Limit the Size of Lists&lt;/H2&gt;&lt;P&gt;One of the most common performance issues that we see is code that generates very large lists of comma separated Strings. This topic has been discussed in a couple of our other articles including &lt;A href="https://www.servicenow.com/community/developer-articles/database-performance-improving-slow-or-and-join-queries/ta-p/2299441" target="_blank" rel="noopener noreferrer"&gt;Database Performance: Improving Slow OR and JOIN Queries [community article]&lt;/A&gt; and &lt;A href="https://www.servicenow.com/community/developer-articles/performance-best-practices-for-server-side-coding-in-servicenow/ta-p/2324426#numberTwo" target="_self"&gt;Performance Best Practices for Server-side Coding in ServiceNow [community article]&lt;/A&gt;. We won't go into detail explaining how to avoid this issue here since that has been done in many other places, including the two articles that we have just linked above. Long story, short, you must somehow limit the max size of your lists. In this article we will show a number of experiments to demonstrate exactly how impactful large comma separated lists can be.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;2. Avoid String Concatenation&lt;/H2&gt;&lt;P&gt;In multiple tests, we have seen that String concatenation took between 2 to 6 times more memory than an Array of Strings with the exact same number of items. We have also seen that creating lists via String Concatenation takes between 1.5 to 4 times longer than with an Array. We ran tests in both client side Mozilla JavaScript engines as well as the Rhino Javascript engine that runs on ServiceNow's servers.&lt;/P&gt;&lt;P&gt;&lt;FONT size="6"&gt;&lt;SPAN&gt;&lt;FONT size="5"&gt;&lt;FONT color="#FF6600"&gt;|&lt;/FONT&gt; String contentation took 2 to 6 times more memory&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;Aside from our tests, we have also seen "in the wild" that String concatenation can lead to StackOverflowError in Java. This issue is documented in &lt;A href="https://support.servicenow.com/kb?id=kb_article_view&amp;amp;sysparm_article=KB1273582" target="_self"&gt;KB1273582&lt;/A&gt;. In turn, StackOverflowError can lead to all sorts of unpredictable conditions that can have serious implications like performance degradation or data corruption. This rare defect seems to happen only when Reference Qualifier code is calling a Script Include that builds a large comma separated String using String concatenation.&lt;/P&gt;&lt;H1&gt;Test Results&lt;/H1&gt;&lt;HR /&gt;&lt;H3&gt;Rhino Engine, JFR Test&lt;/H3&gt;&lt;P&gt;Using Java Flight Recorder (JFR) we ran the below two variations of building a comma separated string.&lt;/P&gt;&lt;P&gt;We ran the below code to test 100,000 strings. So string concatenation used 6.14 times more memory!&lt;/P&gt;&lt;TABLE border="1" width="100%"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="100%"&gt;&lt;P&gt;Result:&lt;/P&gt;&lt;P&gt;Size with Array: 21 MB&lt;/P&gt;&lt;P&gt;Size with String concatenation: 129 MB&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;var arr1 = "";     
for (var i = 0; i &amp;lt; 100000; i++) {             
arr1 += "some kinda longish string";
} 
var res =  "sys_idIN" + arr1;

var arr1 = [];     
for (var i = 0; i &amp;lt; 100000; i++) {             
arr1.push("some kinda longish string");
}
var res =  "sys_idIN" + arr1;&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="javaflightrecorderArrayJoinVsStringConcatenation.png"&gt;&lt;img src="https://www.servicenow.com/community/image/serverpage/image-id/221400i49A630AFC36C0545/image-size/large?v=v2&amp;amp;px=999" alt="javaflightrecorderArrayJoinVsStringConcatenation.png" title="javaflightrecorderArrayJoinVsStringConcatenation.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;HR /&gt;&lt;H3&gt;Firefox Developer Tools test&lt;/H3&gt;&lt;P&gt;We created the following HTML file and alternated between pushing the Test 1 and Test 2 buttons. String concatenation used 2.5 times more memory.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;&amp;lt;html&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;title&amp;gt;Memory Test&amp;lt;/title&amp;gt;
		&amp;lt;script&amp;gt;
			function test1() {
				var testString = "test1";
				var delimiter = ",";
				var result = String(testString);
				for (var ia = 0; ia &amp;lt; 100000; ia++) {
					result += delimiter.concat(testString);
				}
				alert(result.length);
				var stringCount = result.split().length;
				stringCount;
				result;
			}

			function test2() {
				var result = "test2";
				resultArr = [String(result)];
				for (var ia = 0; ia &amp;lt; 100000; ia++) {
					resultArr.push(String(result));
				}
				alert(resultArr.join().length);
				var stringCount = resultArr.length;
				stringCount;
				resultArr;
				result;
			}
		&amp;lt;/script&amp;gt;
	&amp;lt;/head&amp;gt;
	&amp;lt;body&amp;gt;
		&amp;lt;button name='test1' onClick='test1()'&amp;gt;Test 1&amp;lt;/button&amp;gt;
		&amp;lt;button name='test2' onClick='test2()'&amp;gt;Test 2&amp;lt;/button&amp;gt;
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Each time we pushed a button we took a memory snapshot. Here is a screenshot from after the fifth snapshot - after pushing the Test 1 button.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="clientTestFirefox05.png"&gt;&lt;img src="https://www.servicenow.com/community/image/serverpage/image-id/221407iB5AB6EFD85313D7C/image-size/large?v=v2&amp;amp;px=999" alt="clientTestFirefox05.png" title="clientTestFirefox05.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;TABLE border="1" width="100%"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="100%"&gt;&lt;P&gt;Result:&lt;/P&gt;&lt;P&gt;Size with Array: 1 MB&lt;/P&gt;&lt;P&gt;Size with String concatenation: 2.5 MB&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;HR /&gt;&lt;H3&gt;Rhino Engine, "Scripts - Background" Stress Test&lt;/H3&gt;&lt;P&gt;For this final test we wanted to stress test an actual ServiceNow instance to see when it started to tip over. To run the test we ran the following code via the in-app "Scripts - Background" module. We gradually increased the number of Strings in the list and recorded the execution time of each execution until an execution took more than 30 seconds.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;var timeExceeded = false;
var maxExcecutionTimeSeconds =  30;

var method1 = function(loops) {
  var sw = new GlideStopWatch();
  var stringsArr = [];
  for (var ia = 0; ia &amp;lt; numArrays; ia++) {
    stringsArr[ia] = "";
  }
  for (var i = 0; i &amp;lt; loops; i++) {
    if (sw.getTime() &amp;gt; maxExcecutionTimeSeconds * 1000) {
      gs.warn("exceeded " + maxExcecutionTimeSeconds + " seconds while building list of " + loops + " elements.");
      timeExceeded = true;
      return;
    }
    for (var ib = 0; ib &amp;lt; numArrays; ib++) {
      stringsArr[ib] += "some kinda longish string,";
    }
  }
  gs.info(loops*numArrays + ":" + sw.getTime() + ":" + GlideSystemUtil.usedMemoryMB());
}


var method2 = function(loops) {
  var sw = new GlideStopWatch();
  var arrayArr = [];
  for (var ia = 0; ia &amp;lt; numArrays; ia++) {
    arrayArr[ia] = [];
  }
  for (var i = 0; i &amp;lt; loops; i++) {
    if (sw.getTime() &amp;gt; maxExcecutionTimeSeconds * 1000) {
      gs.warn("exceeded " + maxExcecutionTimeSeconds + " seconds while building list of " + loops + " elements.");
      timeExceeded = true;
      return;
    }
    for (var ib = 0; ib &amp;lt; numArrays; ib++) {
      arrayArr[ib].push("some kinda longish string");
    }
  }
  gs.info(loops*numArrays + ":" + sw.getTime() + ":" + GlideSystemUtil.usedMemoryMB());
}

var count = 1;
var factor = 20000;
var numArrays = 20;
var methodToCall = 2;
gs.info("factor: " + factor + ", arrays: " + numArrays + ", method: " + (methodToCall==1?"String concatenation":"Array"));
while(count++ &amp;lt; 100 &amp;amp;&amp;amp; !timeExceeded) {
if (methodToCall == 1)
method1(count*factor);
else
method2(count*factor);
}&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;ServiceNow has a built-in protection against objects getting too large (see &lt;A href="https://support.servicenow.com/kb?id=kb_article_view&amp;amp;sysparm_article=KB0749085" target="_self"&gt;KB0749085 [Official ServiceNow Support site])&lt;/A&gt;. It doesn't kick in in all circumstances, but it does when you are running in Scripts - Background. When ServiceNow detects that an object is above the maximum size, it stops execution and throws an exception similar to the following in the logs:&lt;/P&gt;&lt;P&gt;"String object would exceed maximum permitted size of 33554432"&lt;/P&gt;&lt;P&gt;To get around this restraint we created multiple objects so that their combined size could exceed the limit.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;The results are pretty interesting. Using an Array the instance could build 40 million 25 character strings before it reached 30 seconds of execution time. Using String concatenation the code reached 30 seconds of execution time while building a list of 10 million strings. Also, notice that the String concatenation method shows an exponential curve near the end of the test - potentially indicating that the instance was running out of memory resources. On the other hand, note that the execution time trend for the Array method stayed linear - even all the way up to 40 million elements. Finally, note that at 10 million elements the Array method completed in just 7.5 seconds while the String concatenation method took over 30 seconds - 4 times slower!&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;TABLE border="1" width="100%"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="50%"&gt;&lt;P&gt;Results with String concatenation:&lt;/P&gt;&lt;P&gt;Elements added to list in 30 seconds: less than 10M&lt;/P&gt;&lt;P&gt;Time to add 10 million elements: over 30 seconds&lt;/P&gt;&lt;/TD&gt;&lt;TD width="50%"&gt;&lt;P&gt;Results with Array:&lt;/P&gt;&lt;P&gt;Elements added to list in 30 seconds: 40M&lt;/P&gt;&lt;P&gt;Time to add 10 million elements: 7.5 seconds&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Left Y-axis: execution time in milliseconds&lt;/P&gt;&lt;P&gt;Right Y-axis: Memory in-use (MB)&lt;/P&gt;&lt;P&gt;X-Axis: Number of elements in list (grid lines every 2M elements)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Using String concatenation up to 30 seconds (code cancelled after building about 9.5 million elements)&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="stressTestConcatMax10M.png"&gt;&lt;img src="https://www.servicenow.com/community/image/serverpage/image-id/221465i3E0C2119920FE98E/image-size/medium?v=v2&amp;amp;px=400" alt="stressTestConcatMax10M.png" title="stressTestConcatMax10M.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Using an Array up to 10 million elements&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="stressTestArrayMax10M.png"&gt;&lt;img src="https://www.servicenow.com/community/image/serverpage/image-id/221466i178AF1978D92C06D/image-size/medium?v=v2&amp;amp;px=400" alt="stressTestArrayMax10M.png" title="stressTestArrayMax10M.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Using an Array up to 30 seconds (40 million elements)&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="stressTestConcatMax10M.png"&gt;&lt;img src="https://www.servicenow.com/community/image/serverpage/image-id/221464iAAE7B8E45B0B0E5C/image-size/medium?v=v2&amp;amp;px=400" alt="stressTestConcatMax10M.png" title="stressTestConcatMax10M.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;Summary&lt;/H2&gt;&lt;P&gt;As you can see from these tests, the creation of large lists in code can easily cause a performance headache. If at all possible, use programming strategies that do not require the creation of large lists of items in memory. When you do need to use large lists, set limits to ensure your lists sizes stay reasonably small and use Arrays instead of String concatenation to keep your memory usage as efficient as possible. Ensure that all engineers writing code in your environment are being memory conscious by avoiding excessive lists whenever possible.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Best regards,&lt;/P&gt;&lt;P&gt;Your ServiceNow Global Technical Support Performance Team&lt;/P&gt;&lt;HR /&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;&amp;lt; Previous Article&lt;/TD&gt;&lt;TD&gt;&amp;nbsp;&lt;/TD&gt;&lt;TD&gt;Next Article &amp;gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;A href="https://www.servicenow.com/community/now-platform-articles/caching-data-to-improve-performance/ta-p/2308096" target="_blank" rel="noopener"&gt;Caching data to improve performance&lt;/A&gt;&lt;/TD&gt;&lt;TD&gt;&amp;nbsp;&lt;/TD&gt;&lt;TD&gt;&lt;A href="https://www.servicenow.com/community/developer-articles/database-performance-improving-slow-or-and-join-queries/ta-p/2299441" target="_blank" rel="noopener"&gt;Database Performance: Improving Slow OR and JOIN Queries &lt;/A&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Fri, 24 Feb 2023 19:40:27 GMT</pubDate>
    <dc:creator>GTSPerformance</dc:creator>
    <dc:date>2023-02-24T19:40:27Z</dc:date>
    <item>
      <title>Performance Best Practices for Coding Comma Separated Lists</title>
      <link>https://www.servicenow.com/community/servicenow-ai-platform-articles/performance-best-practices-for-coding-comma-separated-lists/ta-p/2398988</link>
      <description>&lt;DIV class="lia-message-template-content-zone"&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;&amp;lt; Previous Article&lt;/TD&gt;&lt;TD&gt;&amp;nbsp;&lt;/TD&gt;&lt;TD&gt;Next Article &amp;gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;A href="https://www.servicenow.com/community/now-platform-articles/caching-data-to-improve-performance/ta-p/2308096" target="_blank" rel="noopener"&gt;Caching data to improve performance&lt;/A&gt;&lt;/TD&gt;&lt;TD&gt;&amp;nbsp;&lt;/TD&gt;&lt;TD&gt;&lt;A href="https://www.servicenow.com/community/developer-articles/database-performance-improving-slow-or-and-join-queries/ta-p/2299441" target="_blank" rel="noopener"&gt;Database Performance: Improving Slow OR and JOIN Queries &lt;/A&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;HR /&gt;&lt;H2&gt;Overview&lt;/H2&gt;&lt;P&gt;This guide is written by the ServiceNow Technical Support Performance team (&lt;A href="https://community.servicenow.com/community?id=community_article&amp;amp;sys_id=0686dc99dbde241011762183ca9619e6" target="_blank" rel="noopener"&gt;All Articles&lt;/A&gt;). We are a global group of experts that help our customers with performance issues. If you have questions about the content of&amp;nbsp;&lt;EM&gt;this&amp;nbsp;article&lt;/EM&gt;&amp;nbsp;we will try to answer them here. However,&amp;nbsp;if you have urgent questions or specific issues, please see the list of resources on our profile page:&amp;nbsp;&lt;A href="https://community.servicenow.com/community?id=community_user_profile&amp;amp;user=e2299eed1b305490d01143f6fe4bcb4d" target="_blank" rel="noopener"&gt;ServiceNowPerformanceGTS&lt;/A&gt;.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;If you have developed in ServiceNow for a while, then you have probably come across the scenario where you want to code a comma separated list of items. For example, consider the following code:&lt;/P&gt;&lt;TABLE border="1" width="100%"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="100%"&gt;&lt;FONT color="#800000"&gt;WARNING: Do not use! There are two major problems with the below code!&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/DIV&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;function getThingsCreatedByUser(userId) {
  if (!userId) return null;
  var thingsGr = new GlideRecord("u_thing");
  thingsGr.addQuery("sys_created_by", userId);
  thingsGr.query();
  var result = "";
  while(thingsGr.next()) {
    result = result + thingsGr.sys_id + ",";
  }
  return (result.length) ? "sys_idIN" + result.substr(0,result.length-1) : null;
}&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Code like this might be called by a reference qualifier or a dynamic filter to get a list of things created by a certain user. However, this code has at least two&lt;FONT color="#FF0000"&gt;&lt;EM&gt; major weaknesses&lt;/EM&gt;&lt;/FONT&gt;. These could cause your whole ServiceNow node to run low on memory, causing severe performance degradation for all users who are logged in to that node:&lt;/P&gt;&lt;OL&gt;&lt;LI&gt;There is no limit on the number of items that can go in the comma separated list.&lt;/LI&gt;&lt;LI&gt;It uses String concatenation&lt;/LI&gt;&lt;/OL&gt;&lt;P&gt;In the rest of this article we will explain why the above weaknesses are dangerous and what you can do to avoid them through adopting certain coding best practices.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;1. Limit the Size of Lists&lt;/H2&gt;&lt;P&gt;One of the most common performance issues that we see is code that generates very large lists of comma separated Strings. This topic has been discussed in a couple of our other articles including &lt;A href="https://www.servicenow.com/community/developer-articles/database-performance-improving-slow-or-and-join-queries/ta-p/2299441" target="_blank" rel="noopener noreferrer"&gt;Database Performance: Improving Slow OR and JOIN Queries [community article]&lt;/A&gt; and &lt;A href="https://www.servicenow.com/community/developer-articles/performance-best-practices-for-server-side-coding-in-servicenow/ta-p/2324426#numberTwo" target="_self"&gt;Performance Best Practices for Server-side Coding in ServiceNow [community article]&lt;/A&gt;. We won't go into detail explaining how to avoid this issue here since that has been done in many other places, including the two articles that we have just linked above. Long story, short, you must somehow limit the max size of your lists. In this article we will show a number of experiments to demonstrate exactly how impactful large comma separated lists can be.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;2. Avoid String Concatenation&lt;/H2&gt;&lt;P&gt;In multiple tests, we have seen that String concatenation took between 2 to 6 times more memory than an Array of Strings with the exact same number of items. We have also seen that creating lists via String Concatenation takes between 1.5 to 4 times longer than with an Array. We ran tests in both client side Mozilla JavaScript engines as well as the Rhino Javascript engine that runs on ServiceNow's servers.&lt;/P&gt;&lt;P&gt;&lt;FONT size="6"&gt;&lt;SPAN&gt;&lt;FONT size="5"&gt;&lt;FONT color="#FF6600"&gt;|&lt;/FONT&gt; String contentation took 2 to 6 times more memory&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;Aside from our tests, we have also seen "in the wild" that String concatenation can lead to StackOverflowError in Java. This issue is documented in &lt;A href="https://support.servicenow.com/kb?id=kb_article_view&amp;amp;sysparm_article=KB1273582" target="_self"&gt;KB1273582&lt;/A&gt;. In turn, StackOverflowError can lead to all sorts of unpredictable conditions that can have serious implications like performance degradation or data corruption. This rare defect seems to happen only when Reference Qualifier code is calling a Script Include that builds a large comma separated String using String concatenation.&lt;/P&gt;&lt;H1&gt;Test Results&lt;/H1&gt;&lt;HR /&gt;&lt;H3&gt;Rhino Engine, JFR Test&lt;/H3&gt;&lt;P&gt;Using Java Flight Recorder (JFR) we ran the below two variations of building a comma separated string.&lt;/P&gt;&lt;P&gt;We ran the below code to test 100,000 strings. So string concatenation used 6.14 times more memory!&lt;/P&gt;&lt;TABLE border="1" width="100%"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="100%"&gt;&lt;P&gt;Result:&lt;/P&gt;&lt;P&gt;Size with Array: 21 MB&lt;/P&gt;&lt;P&gt;Size with String concatenation: 129 MB&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;var arr1 = "";     
for (var i = 0; i &amp;lt; 100000; i++) {             
arr1 += "some kinda longish string";
} 
var res =  "sys_idIN" + arr1;

var arr1 = [];     
for (var i = 0; i &amp;lt; 100000; i++) {             
arr1.push("some kinda longish string");
}
var res =  "sys_idIN" + arr1;&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="javaflightrecorderArrayJoinVsStringConcatenation.png"&gt;&lt;img src="https://www.servicenow.com/community/image/serverpage/image-id/221400i49A630AFC36C0545/image-size/large?v=v2&amp;amp;px=999" alt="javaflightrecorderArrayJoinVsStringConcatenation.png" title="javaflightrecorderArrayJoinVsStringConcatenation.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;HR /&gt;&lt;H3&gt;Firefox Developer Tools test&lt;/H3&gt;&lt;P&gt;We created the following HTML file and alternated between pushing the Test 1 and Test 2 buttons. String concatenation used 2.5 times more memory.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;&amp;lt;html&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;title&amp;gt;Memory Test&amp;lt;/title&amp;gt;
		&amp;lt;script&amp;gt;
			function test1() {
				var testString = "test1";
				var delimiter = ",";
				var result = String(testString);
				for (var ia = 0; ia &amp;lt; 100000; ia++) {
					result += delimiter.concat(testString);
				}
				alert(result.length);
				var stringCount = result.split().length;
				stringCount;
				result;
			}

			function test2() {
				var result = "test2";
				resultArr = [String(result)];
				for (var ia = 0; ia &amp;lt; 100000; ia++) {
					resultArr.push(String(result));
				}
				alert(resultArr.join().length);
				var stringCount = resultArr.length;
				stringCount;
				resultArr;
				result;
			}
		&amp;lt;/script&amp;gt;
	&amp;lt;/head&amp;gt;
	&amp;lt;body&amp;gt;
		&amp;lt;button name='test1' onClick='test1()'&amp;gt;Test 1&amp;lt;/button&amp;gt;
		&amp;lt;button name='test2' onClick='test2()'&amp;gt;Test 2&amp;lt;/button&amp;gt;
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Each time we pushed a button we took a memory snapshot. Here is a screenshot from after the fifth snapshot - after pushing the Test 1 button.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="clientTestFirefox05.png"&gt;&lt;img src="https://www.servicenow.com/community/image/serverpage/image-id/221407iB5AB6EFD85313D7C/image-size/large?v=v2&amp;amp;px=999" alt="clientTestFirefox05.png" title="clientTestFirefox05.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;TABLE border="1" width="100%"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="100%"&gt;&lt;P&gt;Result:&lt;/P&gt;&lt;P&gt;Size with Array: 1 MB&lt;/P&gt;&lt;P&gt;Size with String concatenation: 2.5 MB&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;HR /&gt;&lt;H3&gt;Rhino Engine, "Scripts - Background" Stress Test&lt;/H3&gt;&lt;P&gt;For this final test we wanted to stress test an actual ServiceNow instance to see when it started to tip over. To run the test we ran the following code via the in-app "Scripts - Background" module. We gradually increased the number of Strings in the list and recorded the execution time of each execution until an execution took more than 30 seconds.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;var timeExceeded = false;
var maxExcecutionTimeSeconds =  30;

var method1 = function(loops) {
  var sw = new GlideStopWatch();
  var stringsArr = [];
  for (var ia = 0; ia &amp;lt; numArrays; ia++) {
    stringsArr[ia] = "";
  }
  for (var i = 0; i &amp;lt; loops; i++) {
    if (sw.getTime() &amp;gt; maxExcecutionTimeSeconds * 1000) {
      gs.warn("exceeded " + maxExcecutionTimeSeconds + " seconds while building list of " + loops + " elements.");
      timeExceeded = true;
      return;
    }
    for (var ib = 0; ib &amp;lt; numArrays; ib++) {
      stringsArr[ib] += "some kinda longish string,";
    }
  }
  gs.info(loops*numArrays + ":" + sw.getTime() + ":" + GlideSystemUtil.usedMemoryMB());
}


var method2 = function(loops) {
  var sw = new GlideStopWatch();
  var arrayArr = [];
  for (var ia = 0; ia &amp;lt; numArrays; ia++) {
    arrayArr[ia] = [];
  }
  for (var i = 0; i &amp;lt; loops; i++) {
    if (sw.getTime() &amp;gt; maxExcecutionTimeSeconds * 1000) {
      gs.warn("exceeded " + maxExcecutionTimeSeconds + " seconds while building list of " + loops + " elements.");
      timeExceeded = true;
      return;
    }
    for (var ib = 0; ib &amp;lt; numArrays; ib++) {
      arrayArr[ib].push("some kinda longish string");
    }
  }
  gs.info(loops*numArrays + ":" + sw.getTime() + ":" + GlideSystemUtil.usedMemoryMB());
}

var count = 1;
var factor = 20000;
var numArrays = 20;
var methodToCall = 2;
gs.info("factor: " + factor + ", arrays: " + numArrays + ", method: " + (methodToCall==1?"String concatenation":"Array"));
while(count++ &amp;lt; 100 &amp;amp;&amp;amp; !timeExceeded) {
if (methodToCall == 1)
method1(count*factor);
else
method2(count*factor);
}&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;ServiceNow has a built-in protection against objects getting too large (see &lt;A href="https://support.servicenow.com/kb?id=kb_article_view&amp;amp;sysparm_article=KB0749085" target="_self"&gt;KB0749085 [Official ServiceNow Support site])&lt;/A&gt;. It doesn't kick in in all circumstances, but it does when you are running in Scripts - Background. When ServiceNow detects that an object is above the maximum size, it stops execution and throws an exception similar to the following in the logs:&lt;/P&gt;&lt;P&gt;"String object would exceed maximum permitted size of 33554432"&lt;/P&gt;&lt;P&gt;To get around this restraint we created multiple objects so that their combined size could exceed the limit.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;The results are pretty interesting. Using an Array the instance could build 40 million 25 character strings before it reached 30 seconds of execution time. Using String concatenation the code reached 30 seconds of execution time while building a list of 10 million strings. Also, notice that the String concatenation method shows an exponential curve near the end of the test - potentially indicating that the instance was running out of memory resources. On the other hand, note that the execution time trend for the Array method stayed linear - even all the way up to 40 million elements. Finally, note that at 10 million elements the Array method completed in just 7.5 seconds while the String concatenation method took over 30 seconds - 4 times slower!&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;TABLE border="1" width="100%"&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD width="50%"&gt;&lt;P&gt;Results with String concatenation:&lt;/P&gt;&lt;P&gt;Elements added to list in 30 seconds: less than 10M&lt;/P&gt;&lt;P&gt;Time to add 10 million elements: over 30 seconds&lt;/P&gt;&lt;/TD&gt;&lt;TD width="50%"&gt;&lt;P&gt;Results with Array:&lt;/P&gt;&lt;P&gt;Elements added to list in 30 seconds: 40M&lt;/P&gt;&lt;P&gt;Time to add 10 million elements: 7.5 seconds&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Left Y-axis: execution time in milliseconds&lt;/P&gt;&lt;P&gt;Right Y-axis: Memory in-use (MB)&lt;/P&gt;&lt;P&gt;X-Axis: Number of elements in list (grid lines every 2M elements)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Using String concatenation up to 30 seconds (code cancelled after building about 9.5 million elements)&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="stressTestConcatMax10M.png"&gt;&lt;img src="https://www.servicenow.com/community/image/serverpage/image-id/221465i3E0C2119920FE98E/image-size/medium?v=v2&amp;amp;px=400" alt="stressTestConcatMax10M.png" title="stressTestConcatMax10M.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Using an Array up to 10 million elements&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="stressTestArrayMax10M.png"&gt;&lt;img src="https://www.servicenow.com/community/image/serverpage/image-id/221466i178AF1978D92C06D/image-size/medium?v=v2&amp;amp;px=400" alt="stressTestArrayMax10M.png" title="stressTestArrayMax10M.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Using an Array up to 30 seconds (40 million elements)&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="stressTestConcatMax10M.png"&gt;&lt;img src="https://www.servicenow.com/community/image/serverpage/image-id/221464iAAE7B8E45B0B0E5C/image-size/medium?v=v2&amp;amp;px=400" alt="stressTestConcatMax10M.png" title="stressTestConcatMax10M.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;H2&gt;Summary&lt;/H2&gt;&lt;P&gt;As you can see from these tests, the creation of large lists in code can easily cause a performance headache. If at all possible, use programming strategies that do not require the creation of large lists of items in memory. When you do need to use large lists, set limits to ensure your lists sizes stay reasonably small and use Arrays instead of String concatenation to keep your memory usage as efficient as possible. Ensure that all engineers writing code in your environment are being memory conscious by avoiding excessive lists whenever possible.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Best regards,&lt;/P&gt;&lt;P&gt;Your ServiceNow Global Technical Support Performance Team&lt;/P&gt;&lt;HR /&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;&amp;lt; Previous Article&lt;/TD&gt;&lt;TD&gt;&amp;nbsp;&lt;/TD&gt;&lt;TD&gt;Next Article &amp;gt;&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;&lt;A href="https://www.servicenow.com/community/now-platform-articles/caching-data-to-improve-performance/ta-p/2308096" target="_blank" rel="noopener"&gt;Caching data to improve performance&lt;/A&gt;&lt;/TD&gt;&lt;TD&gt;&amp;nbsp;&lt;/TD&gt;&lt;TD&gt;&lt;A href="https://www.servicenow.com/community/developer-articles/database-performance-improving-slow-or-and-join-queries/ta-p/2299441" target="_blank" rel="noopener"&gt;Database Performance: Improving Slow OR and JOIN Queries &lt;/A&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Fri, 24 Feb 2023 19:40:27 GMT</pubDate>
      <guid>https://www.servicenow.com/community/servicenow-ai-platform-articles/performance-best-practices-for-coding-comma-separated-lists/ta-p/2398988</guid>
      <dc:creator>GTSPerformance</dc:creator>
      <dc:date>2023-02-24T19:40:27Z</dc:date>
    </item>
    <item>
      <title>Re: Performance Best Practices for Coding Comma Separated Lists</title>
      <link>https://www.servicenow.com/community/servicenow-ai-platform-articles/performance-best-practices-for-coding-comma-separated-lists/tac-p/2914696#M4204</link>
      <description>&lt;P&gt;linked&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;A id="link_7" class="page-link lia-link-navigation lia-custom-event" href="https://www.servicenow.com/community/developer-articles/script-using-split-to-use-an-array-of-sys-id-s/ta-p/2313550" data-sider-select-id="358eb91a-fcbd-4012-917c-81bbfc931cfc" target="_blank"&gt;Script: Using split to use an array of SYS_ID's&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://www.servicenow.com/community/developer-articles/script-using-split-to-use-an-array-of-sys-id-s/ta-p/2313550" target="_blank"&gt;https://www.servicenow.com/community/developer-articles/script-using-split-to-use-an-array-of-sys-id-s/ta-p/2313550&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 30 Apr 2024 22:05:39 GMT</pubDate>
      <guid>https://www.servicenow.com/community/servicenow-ai-platform-articles/performance-best-practices-for-coding-comma-separated-lists/tac-p/2914696#M4204</guid>
      <dc:creator>tiagomacul</dc:creator>
      <dc:date>2024-04-30T22:05:39Z</dc:date>
    </item>
    <item>
      <title>Re: Performance Best Practices for Coding Comma Separated Lists</title>
      <link>https://www.servicenow.com/community/servicenow-ai-platform-articles/performance-best-practices-for-coding-comma-separated-lists/tac-p/3182635#M4617</link>
      <description>&lt;P&gt;Incredible, I thought the opposite was better, to concatenate string than push arrays. Thanks.&lt;/P&gt;</description>
      <pubDate>Wed, 19 Feb 2025 15:31:53 GMT</pubDate>
      <guid>https://www.servicenow.com/community/servicenow-ai-platform-articles/performance-best-practices-for-coding-comma-separated-lists/tac-p/3182635#M4617</guid>
      <dc:creator>Ram_n Botelho</dc:creator>
      <dc:date>2025-02-19T15:31:53Z</dc:date>
    </item>
    <item>
      <title>Re: Performance Best Practices for Coding Comma Separated Lists</title>
      <link>https://www.servicenow.com/community/servicenow-ai-platform-articles/performance-best-practices-for-coding-comma-separated-lists/tac-p/3583174#M5108</link>
      <description>&lt;P&gt;Neetly Executed and well Explained!&lt;/P&gt;</description>
      <pubDate>Tue, 04 Aug 2026 12:22:14 GMT</pubDate>
      <guid>https://www.servicenow.com/community/servicenow-ai-platform-articles/performance-best-practices-for-coding-comma-separated-lists/tac-p/3583174#M5108</guid>
      <dc:creator>kartiklaher</dc:creator>
      <dc:date>2026-08-04T12:22:14Z</dc:date>
    </item>
  </channel>
</rss>

