En supposant que j'ai une classe qui effectue un traitement lourd, fonctionnant avec plusieurs collections. Ce que je veux faire, c'est m'assurer que de telles opérations ne peuvent pas conduire à un manque de mémoire ou, mieux, je veux définir un seuil de mémoire utilisable.
class MyClass()
{
public void myMethod()
{
for(int i=0; i<10000000; i++)
{
// Allocate some memory, may be several collections
}
}
}
class MyClassTest
{
@Test
public void myMethod_makeSureMemoryFootprintIsNotBiggerThanMax()
{
new MyClass().myMethod();
// How do I measure amount of memory it may try to allocate?
}
}
Quelle est la bonne approche pour faire cela? Ou ce n'est pas possible/impossible?
Je peux penser à plusieurs options:
Vous pouvez également écrire votre propre test d'évaluation qui compte la mémoire. L'idée est de
System.gc()
, memoryBefore = runtime.totalMemory() - runtime.freeMemory()
System.gc()
, memoryAfter = runtime.totalMemory() - runtime.freeMemory()
C’est une technique que j’ai utilisée dans mon outil léger de micro-benchmark qui est capable de mesurer l’allocation de mémoire avec une précision en octets.
Vous pouvez utiliser le profileur (par exemple, JProfiler) pour afficher la mémoire utilisée par les classes. Ou, comment mentionné Areo, imprimez simplement l’utilisation de la mémoire:
Runtime runtime = Runtime.getRuntime();
long usedMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Used Memory before" + usedMemoryBefore);
// working code here
long usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Memory increased:" + (usedMemoryAfter-usedMemoryBefore));
Pour mesurer l'utilisation actuelle de la mémoire, utilisez:
Runtime.getRuntime().freeMemory()
, Runtime.getRuntime().totalMemory()
Voici un bon exemple: Obtenir des informations système au niveau du système d'exploitation
Mais cette mesure n’est pas précise, mais elle peut vous donner beaucoup d’informations ... Un autre problème est le nom GC
qui est imprévisible.
Voici un exemple de Netty qui fait quelque chose de similaire: MemoryAwareThreadPoolExecutor . La classe cache de Guava a également une expulsion basée sur la taille. Vous pouvez regarder ces sources et copier ce qu'elles font. En particulier, voici comment Netty est estime la taille des objets . Essentiellement, vous estimeriez la taille des objets que vous générez dans la méthode et gardiez un compte.
Obtenir des informations globales sur la mémoire (par exemple, combien de tas est disponible/utilisé) vous aidera à décider de la quantité de mémoire à allouer à la méthode, mais non à suivre la quantité de mémoire utilisée par les appels de méthode individuels.
Cela dit, il est très rare que vous ayez légitimement besoin de cela. Dans la plupart des cas, limiter l'utilisation de la mémoire en limitant le nombre d'objets pouvant être présents à un moment donné (par exemple, en utilisant une file d'attente limitée) est suffisant et beaucoup, beaucoup plus simple à implémenter.
Cette question est un peu délicate, en raison de la manière dont Java peut allouer de nombreux objets éphémères au cours du traitement, qui seront ensuite collectés lors du nettoyage de la mémoire. Dans la réponse acceptée, nous ne pouvons pas affirmer avec certitude que le ramassage des ordures a été exécuté à un moment donné. Même si nous introduisons une structure de boucle, avec plusieurs appels System.gc()
, un garbage collection peut s’exécuter entre nos appels de méthode.
Une meilleure façon consiste à utiliser plutôt une variante de ce qui est suggéré dans https://cruftex.net/2017/03/28/The-6-Memory-Metrics-You-Should-Track-in-Your-Java- Benchmarks.html , où System.gc()
est déclenché mais nous attendons également que le nombre de GC rapporté augmente:
long getGcCount() {
long sum = 0;
for (GarbageCollectorMXBean b : ManagementFactory.getGarbageCollectorMXBeans()) {
long count = b.getCollectionCount();
if (count != -1) { sum += count; }
}
return sum;
}
long getReallyUsedMemory() {
long before = getGcCount();
System.gc();
while (getGcCount() == before);
return getCurrentlyAllocatedMemory();
}
long getCurrentlyAllocatedMemory() {
final Runtime runtime = Runtime.getRuntime();
return (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024);
}
Cela ne donne toujours qu’une approximation de la mémoire réellement allouée par votre code à un moment donné, mais la valeur est généralement beaucoup plus proche de ce à quoi on pourrait s’intéresser habituellement.