Quels sont les avantages et les inconvénients de vider une collection (dans mon cas, c'est une ArrayList) par rapport à la création d'une nouvelle (et de laisser le garbage collector effacer l'ancienne).
Plus précisément, j'ai un ArrayList<Rectangle>
Appelé list
. Quand une certaine condition se produit, j'ai besoin de vider list
et de le remplir avec d'autres contenus. Dois-je appeler list.clear()
ou simplement créer un nouveau ArrayList<Rectangle>
Et laisser l'ancien être récupéré? Quels sont les avantages et les inconvénients de chaque approche?
Vous conservez le conteneur et appelez clear
lorsque vous souhaitez réduire la charge sur GC: clear()
annule toutes les références à l'intérieur du tableau, mais ne rend pas le tableau éligible pour la récupération par le Éboueur. Cela peut accélérer les insertions futures, car le tableau à l'intérieur de ArrayList
n'a pas besoin de croître. Cette approche est particulièrement avantageuse lorsque les données que vous prévoyez d'ajouter au conteneur ont à peu près la même taille que vous supprimez.
En outre, vous devrez peut-être utiliser clear
lorsque d'autres objets contiennent une référence au tableau que vous êtes sur le point d'effacer.
La libération du conteneur et la création d'un nouveau ont du sens lorsque la taille des nouvelles données peut être différente de celle qui existait auparavant. Bien sûr, vous pouvez obtenir un effet similaire en appelant clear()
en combinaison avec trimToSize()
.
L'avantage de recycler un ArrayList
(par exemple en appelant clear
) est que vous évitez les frais généraux liés à l'allocation d'un nouveau, et le coût de sa croissance ... si vous ne fournissez pas de bon initialCapacity
indice.
Les inconvénients du recyclage d'un ArrayList
sont les suivants:
La méthode clear()
doit affecter null
à chaque emplacement (utilisé) dans le tableau de support ArrayList
s.
La clear()
ne redimensionne pas le tableau de sauvegarde pour libérer de la mémoire. Donc, si vous remplissez et effacez une liste à plusieurs reprises, elle finira par utiliser (en permanence) suffisamment de mémoire pour représenter la plus grande liste qu'elle rencontre. Dans d'autres Word, vous avez augmenté l'empreinte mémoire. Vous pouvez lutter contre cela en appelant trimToSize()
, mais cela crée un objet poubelle, etc.1.
Il existe des problèmes de localité et de génération en génération qui pourraient affecter les performances. Lorsque vous recyclez à plusieurs reprises un ArrayList
, l'objet et son tableau de sauvegarde sont susceptibles d'être conservés. Cela signifie que:
Les objets de liste et les objets représentant des éléments de liste sont susceptibles de se trouver dans différentes zones du tas, ce qui augmente potentiellement les échecs TLB et le trafic de page, en particulier au moment du GC.
L'affectation de références (jeune génération) dans le tableau de sauvegarde de la liste (permanente) est susceptible d'entraîner des frais généraux de barrière d'écriture ... selon la mise en œuvre du GC.
Il n'est pas possible de modéliser avec précision les compromis de performances pour une application réelle. Il y a juste trop de variables. Cependant, la "sagesse reçue" est que le recyclage n'est PAS normalement une bonne idée si vous avez beaucoup de mémoire2 et un ramasse-mi-décent.
Il convient également de noter qu'une machine virtuelle Java moderne peut allouer des objets très efficacement. Il suffit de mettre à jour le pointeur "libre" du tas et d'écrire 2 ou 3 mots d'en-tête d'objet. La remise à zéro de la mémoire est effectuée par le GC ... et en plus du travail à faire, cela est à peu près équivalent au travail que clear()
fait pour annuler les références dans la liste qui est être recyclé.
1 - Il serait préférable pour les performances de créer une nouvelle ArrayList que d'appeler clear () suivi de trimToSize (...). Avec ce dernier, vous obtenez à la fois les frais généraux de récupération de place ET les frais généraux d'annulation nulle.
2 - Un collecteur de copie est plus efficace si la proportion d'objets vides par rapport aux objets non vides est élevée. Si vous analysez le fonctionnement de ce type de collecteur, les coûts sont presque tous engagés pour rechercher et copier des objets accessibles. La seule chose qui doit être faite pour ordonner les objets est de bloquer-zéro-écrire l'espace évacué "de" prêt pour l'allocation de nouveaux objets.
Mon conseil serait de NE PAS recycler les objets ArrayList
sauf si vous avez un besoin démontrable de minimiser le taux de création d'objets (ordures); par exemple. car c'est la seule option dont vous disposez pour réduire les pauses GC (nuisibles).
Toutes choses étant égales par ailleurs, sur une machine virtuelle Hotspot moderne, je crois comprendre que vous obtiendrez les meilleures performances en procédant comme suit:
initialSize
précis lorsque vous allouez les objets de liste. Il vaut mieux légèrement surestimer que légèrement sous-estimer.Comme des points intéressants ont déjà été écrits, vous pouvez y penser encore plus profondément.
Je ne l'ai pas réalisé que j'ai lu un article sur le modèle de perturbateur, voir Comment fonctionne le modèle de perturbateur de LMAX?
Il est possible non seulement de réutiliser la collection sous-jacente , vous pouvez réutiliser également les entités dans la collection.
Par exemple. supposons le cas d'utilisation des producteurs et des consommateurs. Le producteur peut remplir des données dans le même tableau (cyclique) encore et encore et même utiliser les mêmes entités. Effacez simplement les propriétés, l'état interne et remplissez les siens.
C'est une meilleure solution au niveau du point de vue du GC. Mais c'est évidemment un cas particulier pas utile pour tous les problèmes.