The amount of memory cache can use is defined in config:
<!-- ACCESS RESULT CACHE SIZE
Determines the size of the access result cache.
Specify the value in bytes or append the value with KB, MB or GB
A value of 0 (zero) disables the cache.
-->
<setting name="Caching.AccessResultCacheSize" value="10MB" />
That is needed to protect against disk thrashing – running out of physical RAM so that disk is used to power virtual memory (terribly slow). That is a big hazard in Azure WebApps – much less RAM compared to old-school big boxes.
Sitecore keeps track of all the space dedicated to caches:

The last entry highlights 3.4 GB is reserved for Sitecore 9.3 OOB caching layer. Neither native ASP.NET cache, nor ongoing activities, nor the assemblies to load into memory are taken into the account (that is a lot – bin folder is massive):

3.4 GB + 170 MB + ASP.NET caching + ongoing activities + (~2MB/Thread * 50 threads) + buffers + …. = feels to be more than 3.5 GB RAM that S2 tier gives. Nevertheless, S2 is claimed to be supported by Sitecore. How come stock cache configuration can consume all RAM and still be supported?
The first solution that comes to the head – cache detection oversizes objects to be on the safe side; whereas actual sizes are smaller. Let’s compare actual size with estimated.
How does Sitecore know the data size it adds to cache?
There are generally 2 approaches:
- FAST: let developer decide
- SLOW: use reflection to build full object graph -> calc primitive type sizes
Making class to be either Sitecore.Caching.Interfaces.ISizeTrackable
(immutable) or Sitecore.Caching.ICacheable
(mutable) allows developer to detect object size by hand, but how accurate could that be?

Action plan
- Restore some cacheable object from memory snapshot
- Let OOB logic decide the size
- Measure size by hand (from the memory snapshot)
- Compare results
The restored object from snapshot is evaluated to be 313 bytes:

While calculating by-hand shows different results:

Actual size is at least two times larger (632 = (120 + 184 + 248 + 80)) even without:
- DatabaseName – belongs to database that is cached inside factory;
- Account name – one account (string) can touch hundreds of items in one go;
- AccessRight – a dictionary with fixed well-known rights (inside
Sitecore.Security.AccessControl.AccessRightProvider
);
In this case measurement error is over 100% (313 VS 632). In case the same accuracy of measurements persists for other caches, the system could easily spent double configured 3.4 GB -> ~ 6.8 GB.
Remark: 7 GB is the maximum RAM non-premium Azure Web App plans provide.
Summary
Despite caches are configured to strict limits, system could start consuming more RAM than machine physically has causing severe performance degradation. Luckily, memory snapshots would indicate the offending types in heap and help narrow-down the cause.