MemoryCache ne respecte pas les limites de la mémoire de configuration
Je travaille avec le .NET 4.0 MemoryCache classe dans une application et d'essayer de limiter la taille maximale du cache, mais dans mes tests, il ne semble pas que le cache est en fait obéir les limites.
Je suis en utilisant les paramètres qui, selon MSDN, sont censés limiter la taille du cache:
- CacheMemoryLimitMegabytes: Le maximum de la taille de la mémoire, en mégaoctets, qu'une instance d'un objet peut se développer à."
- PhysicalMemoryLimitPercentage: "Le pourcentage de mémoire physique que le cache peut utiliser, exprimée comme une valeur entière de 1 à 100. La valeur par défaut est zéro, ce qui indique que MemoryCache instances de gérer leur propre mémoirele1 sur la base de la quantité de mémoire installée sur l'ordinateur." le1. Ce n'est pas tout à fait correct-- toute valeur inférieure à 4 est ignorée et remplacée par 4.
Je comprends que ces valeurs sont approximatives et pas dur limites comme le fil qui purge le cache est tiré toutes les x secondes et est également dépendant de l'intervalle d'interrogation et d'autres sans-papiers variables. Cependant, même en tenant compte de ces écarts, je vais voir follement incohérentes tailles de cache lorsque le premier élément est supprimé du cache après la mise CacheMemoryLimitMegabytes et PhysicalMemoryLimitPercentage ensemble ou individuellement dans une application de test. Pour être sûr, j'ai couru chaque test 10 fois et calculé la moyenne.
Ce sont les résultats d'essais de l'exemple de code ci-dessous sur un 32 bits de Windows 7 PC avec 3 go de RAM. Taille de la mémoire cache est pris après le premier appel à CacheItemRemoved() sur chaque test. (Je suis conscient de la taille réelle de cache sera plus que cela)
MemLimitMB MemLimitPct AVG Cache MB on first expiry
1 NA 84
2 NA 84
3 NA 84
6 NA 84
NA 1 84
NA 4 84
NA 10 84
10 20 81
10 30 81
10 39 82
10 40 79
10 49 146
10 50 152
10 60 212
10 70 332
10 80 429
10 100 535
100 39 81
500 39 79
900 39 83
1900 39 84
900 41 81
900 46 84
900 49 1.8 GB approx. in task manager no mem errros
200 49 156
100 49 153
2000 60 214
5 60 78
6 60 76
7 100 82
10 100 541
Voici le test de l'application:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Runtime.Caching;
using System.Text;
namespace FinalCacheTest
{
internal class Cache
{
private Object Statlock = new object();
private int ItemCount;
private long size;
private MemoryCache MemCache;
private CacheItemPolicy CIPOL = new CacheItemPolicy();
public Cache(long CacheSize)
{
CIPOL.RemovedCallback = new CacheEntryRemovedCallback(CacheItemRemoved);
NameValueCollection CacheSettings = new NameValueCollection(3);
CacheSettings.Add("CacheMemoryLimitMegabytes", Convert.ToString(CacheSize));
CacheSettings.Add("physicalMemoryLimitPercentage", Convert.ToString(49)); //set % here
CacheSettings.Add("pollingInterval", Convert.ToString("00:00:10"));
MemCache = new MemoryCache("TestCache", CacheSettings);
}
public void AddItem(string Name, string Value)
{
CacheItem CI = new CacheItem(Name, Value);
MemCache.Add(CI, CIPOL);
lock (Statlock)
{
ItemCount++;
size = size + (Name.Length + Value.Length * 2);
}
}
public void CacheItemRemoved(CacheEntryRemovedArguments Args)
{
Console.WriteLine("Cache contains {0} items. Size is {1} bytes", ItemCount, size);
lock (Statlock)
{
ItemCount--;
size = size - 108;
}
Console.ReadKey();
}
}
}
namespace FinalCacheTest
{
internal class Program
{
private static void Main(string[] args)
{
int MaxAdds = 5000000;
Cache MyCache = new Cache(1); //set CacheMemoryLimitMegabytes
for (int i = 0; i < MaxAdds; i++)
{
MyCache.AddItem(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
}
Console.WriteLine("Finished Adding Items to Cache");
}
}
}
Pourquoi est MemoryCache ne pas obéir à l'configuré les limites de la mémoire?
- pour la boucle qui est mal ,sans i++
- J'ai ajouté une connexion MS rapport de ce bug (peut-être que quelqu'un d'autre l'a déjà fait, mais bon...) connect.microsoft.com/VisualStudio/feedback/details/806334/...
- Il est intéressant de noter que Microsoft a maintenant (comme de 9/2014) ajout d'une assez approfondie de réponse sur le connect billet ci-dessus. Le TLDR, c'est que MemoryCache ne pas intrinsèquement vérifier ces limites sur chaque opération, mais plutôt que les limites ne sont respectés lors de cache interne de parage, qui est périodique basée sur la dynamique interne des chronomètres.
- Grâce Poussiéreux. Réponse intéressante à partir de MS sur la question.
- Pouvez-vous nous éclairer sur les raisons d'utiliser "size = taille + (Nom.La Longueur + La Valeur.Longueur * 2);" et "size = taille - 108;"
- Regarde comme ils ont mis à jour la documentation pour MemoryCache.CacheMemoryLimit: "MemoryCache ne pas appliquer instantanément CacheMemoryLimit chaque fois qu'un nouvel élément est ajouté à un MemoryCache instance. L'heuristique interne qui expulse des éléments supplémentaires de la MemoryCache t-il progressivement..." msdn.microsoft.com/en-us/library/...
- Lien vers l'article ne semble pas correct...pouvez-vous résoudre ce problème?
- Je pense que MSFT retiré de la question. En tout cas, MSFT fermé le problème après une discussion avec moi, où ils m'ont dit que la limite n'est appliquée après PoolingTime a expiré.
Vous devez vous connecter pour publier un commentaire.
Wow, je viens de passer beaucoup trop de temps à creuser autour de la CLR avec réflecteur, mais je pense que j'ai enfin une bonne poignée sur ce qui se passe ici.
Les paramètres sont lus correctement, mais il semble y avoir un profond problème dans le CLR lui-même qui ressemble, il sera rendu à la mémoire de réglage de limite essentiellement inutiles.
Le code suivant est reflétée par le Système.Moment de l'exécution.La mise en cache DLL, pour le CacheMemoryMonitor classe (il y a une catégorie similaire qui surveille la mémoire physique et les offres spéciales avec les autres, mais c'est le plus important):
La première chose que vous remarquerez, c'est qu'il n'a même pas essayer de regarder la taille du cache jusqu'à ce qu'après un Gen2 la collecte des ordures, au lieu juste de tomber sur la stockées existantes valeur de taille dans cacheSizeSamples. De sorte que vous ne jamais être en mesure de frapper la cible est bien, mais si le reste travaillé nous aurait au moins d'obtenir une mesure de la taille avant que nous arrivions en réelle difficulté.
Donc en supposant une Gen2 GC a eu lieu, nous courons dans le problème 2, qui est que ref2.ApproximateSize fait un horrible travail de rapprochement de la taille de la mémoire cache. Obstinés par la CLR indésirable, je trouve que c'est un Système.SizedReference, et c'est ce qu'il fait pour obtenir la valeur (IntPtr est un handle vers le MemoryCache objet lui-même):
Je suis en supposant que la déclaration extern signifie qu'il va plonger dans non géré windows la terre à ce point, et je n'ai aucune idée de comment commencer à trouver ce qu'elle fait là. De ce que j'ai observé cependant, il fait une horrible tâche d'essayer de rapprocher la taille de l'ensemble de la chose.
La troisième perceptible chose il y a l'appel à manager.UpdateCacheSize qui sonne comme il devrait faire quelque chose. Malheureusement, dans le fonctionnement normal de l'échantillon de la façon dont cela devrait fonctionner s_memoryCacheManager sera toujours null. Le champ est défini par le public membre statique ObjectCache.De l'hôte. Ce n'est exposée à l'utilisateur de jouer avec s'il le veut, et j'ai été réellement en mesure de faire cette chose type de travail, comme il est censé le faire en s'inclinant ma propre IMemoryCacheManager mise en œuvre, la valeur ObjectCache.D'accueil, puis l'exécution de l'échantillon. À ce point, cependant, il semble que vous pourriez tout aussi bien faire votre propre cache de la mise en œuvre et même pas la peine avec tous ces trucs, surtout depuis que je n'ai aucune idée de si le réglage de votre propre classe pour ObjectCache.Hôte (statique, de sorte qu'il concerne chacun de ces qui pourraient être là-bas dans les processus) pour mesurer le cache pourrait gâcher d'autres choses.
Je crois qu'au moins une partie de celle-ci (si ce n'est un couple de pièces) est juste un droit en haut de bug. Ce serait bien de l'entendre de quelqu'un à MS ce qui se passait avec cette chose.
TLDR version de ce géant de réponse: Supposons que CacheMemoryLimitMegabytes est complètement arrêté à ce point dans le temps. Vous pouvez le mettre à 10 MO, et ensuite de remplir le cache à ~2 GO et souffler un souvenir d'exception avec pas de déclenchement de l'élément de retrait.
Je sais que cette réponse est fou de retard, mais mieux vaut tard que jamais. Je voulais vous laisser savoir que j'ai écrit une version de
MemoryCache
qui résout la Gen 2 de la Collecte automatiquement pour vous. Il a donc garnitures à chaque fois que l'intervalle d'interrogation indique la pression de la mémoire. Si vous rencontrez ce problème, lui donner un aller!http://www.nuget.org/packages/SharpMemoryCache
Vous pouvez aussi le trouver sur GitHub si vous êtes curieux de savoir comment je l'ai résolu. Le code est un peu simple.
https://github.com/haneytron/sharpmemorycache
2n + 20
de taille à l'égard d'octets, oùn
est la longueur de la chaîne. Cela est principalement dû à la prise en charge Unicode.J'ai rencontré ce problème ainsi. Je suis la mise en cache des objets qui sont en cours de licenciement dans mon processus des dizaines de fois par seconde.
J'ai trouvé la configuration et l'utilisation de la libère les éléments, toutes les 5 secondes la plupart du temps.
App.config:
Prendre note de cacheMemoryLimitMegabytes. Lorsque cela a été fixée à zéro, la purge de routine ne serait pas le feu dans un délai raisonnable.
L'ajout de cache:
Confirmant la suppression de cache est de travail:
J'ai (heureusement) suis tombé sur ce post hier, lors de la première tentative d'utilisation de la MemoryCache. Je pensais que ce serait un cas simple des valeurs de réglage et à l'aide des classes, mais j'ai rencontré des problèmes similaires décrites ci-dessus. Pour essayer et voir ce qui se passait, j'ai extrait de la source à l'aide de ILSpy et ensuite mettre en place un test et traversai le code. Mon code de test est très similaire au code ci-dessus de sorte que je ne poste pas. De mes tests, j'ai remarqué que la mesure de la taille du cache n'a jamais été particulièrement précis (comme mentionné ci-dessus) et compte tenu de la mise en œuvre actuelle n'auraient jamais fonctionner de manière fiable. Toutefois, la mesure physique était bien et si la mémoire physique a été mesurée à chaque sondage, il me semblait que le code ne fonctionne de manière fiable. Donc, j'ai enlevé la gen 2 de collecte des ordures chèque dans un délai de MemoryCacheStatistics; dans des conditions normales, pas de mémoire que des mesures seront prises, sauf si il y a eu un autre gen 2 la collecte des ordures depuis la dernière mesure.
Dans un scénario de test de toute évidence, cela fait une grande différence comme le cache est en train de frapper constamment pour que les objets n'ont jamais la chance de les obtenir à gen 2. Je pense que nous allons nous servir de la modification de la construction de cette dll sur notre projet et d'utiliser la langue officielle de MS construire .net 4.5 sort (qui, selon le connecter à l'article mentionné ci-dessus devrait avoir la correction en elle). Logiquement, je peux voir pourquoi la gen 2 de vérification a été mis en place, mais dans la pratique, je ne suis pas sûr s'il fait beaucoup de sens. Si la mémoire atteint 90% (ou autre limite qu'elle a été définie), alors il ne devrait pas d'importance si un gen 2 de la collection a eu lieu ou non, les articles doivent être expulsés, peu importe.
J'ai quitté mon code de test en cours d'exécution pour environ 15 minutes avec le physicalMemoryLimitPercentage à 65%. J'ai vu l'utilisation de la mémoire restent entre 65-68% au cours de l'essai ont vu des choses se expulsées correctement. Dans mon test, j'ai mis le pollingInterval à 5 secondes, physicalMemoryLimitPercentage de 65 ans et physicalMemoryLimitPercentage à 0 par défaut de cette.
Suivant les conseils ci-dessus; une mise en œuvre de IMemoryCacheManager pourraient être apportées pour en expulser les choses à partir du cache. Il serait toutefois souffrir de la gen 2 pour vérifier le problème mentionné. Bien que, selon le scénario, cela peut ne pas être un problème dans le code de production et peuvent travailler suffisamment pour les gens.
J'ai fait quelques tests avec l'exemple de @Canacourse et la modification de @woany et je pense qu'il y a quelques critique des appels qui bloquent le nettoyage de la mémoire cache.
Mais pourquoi la modification de @woany semble garder la mémoire au même niveau? Tout d'abord, la RemovedCallback n'est pas réglé et il n'y a pas de sortie de la console ou de l'attente pour une entrée qui pourrait bloquer le fil de la mémoire cache.
Deuxièmement...
Un Thread.Sleep(1) toutes les ~1000e AddItem() aurait le même effet.
Bien, elle n'est pas très profonde investigation du problème, mais il semble comme si le fil de la MemoryCache ne pas obtenir suffisamment de temps PROCESSEUR pour le nettoyage, tandis que de nombreux nouveaux éléments sont ajoutés.
Il s'est avéré qu'il n'est pas un bug , tout ce que vous devez faire est de définir la mise en commun laps de temps pour appliquer les limites , il semble que si vous laissez la mise en commun n'est pas défini, il ne sera jamais à déclencher.Je l'ai juste testé et pas besoin d'enveloppes ou de code supplémentaire :
Définir la valeur de "
PollingInterval
" basé sur la vitesse de la mémoire cache est de plus en plus , si c'croissance trop rapide augmentation de la fréquence de l'interrogation vérifie dans le cas contraire le vérifie pas très fréquent de ne pas provoquer de surcharge.Si vous utilisez la suite modifiés de classe et de surveiller la mémoire via le Gestionnaire des Tâches n'est en fait obtenir garnis:
MemoryCache
. Je me demande pourquoi cet exemple fonctionne.