Spark Streaming - long garbage collection time
<p>I'm running a Spark Streaming application with 1-hour batches to join two data feeds and write the output to disk. The total size of one data feed is about 40 GB per hour (split in multiple files), while the size of the second data feed is about 600-800 MB per hour (also split in multiple files). Due to application constraints, I may not be able to run smaller batches. Currently, it takes about 20 minutes to produce the output in a cluster with 140 cores and 700 GB of RAM. I'm running 7 workers and 28 executors, each with 5 cores and 22 GB of RAM.</p>

<p>I execute mapToPair(), filter(), and reduceByKeyAndWindow(1 hour batch) on the 40 GB data feed. Most of the computation time is spent on these operations. What worries me is the Garbage Collection (GC) execution time per executor, which goes from 25 secs to 9.2 mins. I attach two screenshots below: one lists the GC time and one prints out GC comments for a single executor. I anticipate that the executor that spends 9.2 mins in doing garbage collection is eventually killed by the Spark driver.</p>

<p>I think these numbers are too high. Do you have any suggestion about keeping GC time low? I'm already using Kryo Serializer, ++UseConcMarkSweepGC, and spark.rdd.compress=true.</p>

<p>Is there anything else that would help?
<a href="https://i.stack.imgur.com/hLiHt.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/hLiHt.png" alt="gc time"></a>
<a href="https://i.stack.imgur.com/v9QHn.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/v9QHn.png" alt="executor"></a></p>

<p>EDIT</p>

<p>This is a snippet of my code:</p>

<pre><code>// The data feed is then mapped to a Key/Value RDD. Some string in the original RDD Stream will be filtered out according to the business logic 
JavaPairDStream&lt;String, String&gt; filtered_data = orig_data.mapToPair(parserFunc)
                                                   .filter(new Function&lt;scala.Tuple2&lt;String, String&gt;, Boolean&gt;() {
                                                     @Override
                                                     public Boolean call(scala.Tuple2&lt;String, String&gt; t) {
                                                       return (!t._2().contains("null"));
                                                     }
                                                   });

// WINDOW_DURATION = 2 hours, SLIDE_DURATION = 1 hour. The data feed will be later joined with another feed. 
// These two feeds are asynchronous: records in the second data feed may match records that appeared in the first data feed up to 2 hours before. 
// I need to save RDDs of the first data feed because they may be joined later. 
// I'm using reduceByKeyAndWindow() instead of window() because I can build this "cache" incrementally. 
// For a given key, appendString() simply appends new a new string to the value, while removeString() removes the strings (i.e. parts of the values) that go out of scope (i.e. out of  WINDOW_INTERVAL)
JavaPairDStream&lt;String, String&gt; windowed_data = filtered_data.reduceByKeyAndWindow(appendString, removeString, Durations.seconds(WINDOW_DURATION), Durations.seconds(SLIDE_DURATION))
                                                 .flatMapValues(new Function&lt;String, Iterable&lt;String&gt;&gt;() {
                                                   @Override
                                                   public Iterable&lt;String&gt; call(String s) {
                                                     return Arrays.asList(s.split(","));
                                                   }
                                                 });

// This is a second data feed, which is also transformed to a K/V RDD for the join operation with the first feed
JavaDStream&lt;String&gt; second_stream = jssc.textFileStream(MSP_DIR);

JavaPairDStream&lt;String, String&gt; ss_kv = second_stream.mapToPair(new PairFunction&lt;String, String, String&gt;() {
                                                @Override
                                                public scala.Tuple2&lt;String, String&gt; call(String row) {
                                                  String[] el = row.split("\\|");
                                                  return new scala.Tuple2(el[9], row);
                                                }
                                              });


JavaPairDStream&lt;String, String&gt; joined_stream = ss_kv.join(windowed_data)

// Use foreachRDD() to save joined_stream to HDFS
</code></pre>