diff --git a/lib/jsduck/batch_parser.rb b/lib/jsduck/batch_parser.rb
index 8abe6677ad6283e73b61488bc0c2e3d7ec15aa9a..f229bd566a688379e4e83a447956b13ba7ccfab9 100644
--- a/lib/jsduck/batch_parser.rb
+++ b/lib/jsduck/batch_parser.rb
@@ -14,21 +14,31 @@ module JsDuck
     def self.parse(opts)
       cache = Cache.create(opts)
 
-      Util::Parallel.map(opts.input_files) do |fname|
+      results = Util::Parallel.map(opts.input_files) do |fname|
         Logger.log("Parsing", fname)
+
         begin
           source = Util::IO.read(fname)
           docs = nil
+
           unless docs = cache.read(source)
             docs = Parser.new.parse(source, fname, opts)
             cache.write(source, docs)
           end
-          Source::File.new(source, docs, fname)
+
+          {
+            :file => Source::File.new(source, docs, fname),
+            :cache => cache.previous_entry,
+          }
         rescue
           Logger.fatal_backtrace("Error while parsing #{fname}", $!)
           exit(1)
         end
       end
+
+      cache.cleanup( results.map {|r| r[:cache] }.compact )
+
+      return results.map {|r| r[:file] }
     end
 
   end
diff --git a/lib/jsduck/cache.rb b/lib/jsduck/cache.rb
index cb563daf69ef546d81e9090772e98765192f2c00..47127f9cd2a9a094d514208a1ac2df94d047b23c 100644
--- a/lib/jsduck/cache.rb
+++ b/lib/jsduck/cache.rb
@@ -1,6 +1,7 @@
 require 'digest/md5'
 require 'fileutils'
 require 'jsduck/util/null_object'
+require 'set'
 
 module JsDuck
 
@@ -15,12 +16,23 @@ module JsDuck
       if opts.cache && opts.cache_dir
         Cache.new(opts.cache_dir)
       else
-        Util::NullObject.new(:read => nil, :write => nil)
+        Util::NullObject.new(
+          :read => nil,
+          :write => nil,
+          :previous_entry => nil,
+          :cleanup => nil
+          )
       end
     end
 
+    # The name of the cache file that was previously read or written.
+    # When the #read call failed to find the file, it will be nil.
+    # But it will always be available after the #write call.
+    attr_reader :previous_entry
+
     def initialize(cache_dir)
       @cache_dir = cache_dir
+      @previous_entry = nil
       FileUtils.mkdir_p(cache_dir) unless File.exists?(cache_dir)
     end
 
@@ -29,8 +41,10 @@ module JsDuck
     def read(file_contents)
       fname = file_name(file_contents)
       if File.exists?(fname)
+        @previous_entry = fname
         File.open(fname, "rb") {|file| Marshal::load(file) }
       else
+        @previous_entry = nil
         nil
       end
     end
@@ -39,9 +53,21 @@ module JsDuck
     # contents of a source file.
     def write(file_contents, data)
       fname = file_name(file_contents)
+      @previous_entry = fname
       File.open(fname, "wb") {|file| Marshal::dump(data, file) }
     end
 
+    # Given listing of used cache files (those that were either read
+    # or written during this jsduck run) removes rest of the files
+    # from cache directory that were unused.
+    def cleanup(used_cache_entries)
+      used = Set.new(used_cache_entries)
+
+      Dir[@cache_dir + "/*.dat"].each do |file|
+        FileUtils.rm_rf(file) unless used.include?(file)
+      end
+    end
+
     private
 
     def file_name(file_contents)