F# Compiler Guide

Compiler Services: Notes on the FSharpChecker caches

This is a design note on the FSharpChecker component and its caches. See also the notes on the FSharpChecker operations queue

Each FSharpChecker object maintains a set of caches. These are

Profiling the memory used by the various caches can be done by looking for the corresponding static roots in memory profiling traces.

The sizes of some of these caches can be adjusted by giving parameters to FSharpChecker. Unless otherwise noted, the cache sizes above indicate the "strong" size of the cache, where memory is held regardless of the memory pressure on the system. Some of the caches can also hold "weak" references which can be collected at will by the GC.

Note: Because of these caches, you should generally use one global, shared FSharpChecker for everything in an IDE application.

Low-Memory Condition

Version added a "maximum memory" limit specified by the MaxMemory property on FSharpChecker (in MB). If an FCS project operation is performed (see CheckMaxMemoryReached in service.fs) and System.GC.GetTotalMemory(false) reports a figure greater than this, then the strong sizes of all FCS caches are reduced to either 0 or 1. This happens for the remainder of the lifetime of the FSharpChecker object. In practice this will still make tools like the Visual Studio F# Power Tools usable, but some operations like renaming across multiple projects may take substantially longer.

By default the maximum memory trigger is disabled, see maxMBDefault in service.fs.

Reducing the FCS strong cache sizes does not guarantee there will be enough memory to continue operations - even holding one project strongly may exceed a process memory budget. It just means FCS may hold less memory strongly.

If you do not want the maximum memory limit to apply then set MaxMemory to System.Int32.MaxValue.


In this design note, you learned that the FSharpChecker component keeps a set of caches in order to support common incremental analysis scenarios reasonably efficiently. They correspond roughly to the original caches and sizes used by the Visual F# Tools, from which the FSharpChecker component derives.

In long running, highly interactive, multi-project scenarios you should carefully consider the cache sizes you are using and the tradeoffs involved between incremental multi-project checking and memory usage.