La mise en commun d'objets est-elle une technique obsolète?

62

Je connais très bien le concept de pooling d’objets et j’essaie toujours de l’utiliser le plus possible.

De plus, j’ai toujours pensé que le pool d’objets était la norme, car j’ai observé que Java lui-même, ainsi que les autres frameworks, utilisait le pooling autant que possible.

Récemment, j’ai lu quelque chose de complètement nouveau (et contre-intuitif?) Pour moi.

Ce regroupement nuit en fait aux performances des programmes, en particulier dans les applications concurrentes, et il est conseillé d’instancier les newobjets, car dans les machines JVM plus récentes, l’instanciation d’un objet est très rapide.

Je lis ceci dans le livre: Java Concurrency in Practice

Maintenant, je commence à penser que si je ne comprends pas bien quelque chose ici, car la première partie du livre recommandait d’utiliser Executorscette méthode de réutilisation Threadau lieu de créer de nouvelles instances.

Alors, le pool d'objets est-il devenu obsolète de nos jours?

utilisateur10326
la source

Réponses:

72

Elle est déconseillée en tant que technique générale car, comme vous l'avez remarqué, la création et la destruction d'objets de courte durée (c.-à-d. L'allocation de mémoire et la GC) sont extrêmement économiques dans les machines virtuelles modernes. Il est donc probable que l'utilisation d'un pool d'objets écrit à la main pour vos objets usuels soit plus lente, plus compliquée et plus sujette aux erreurs qu'une erreur ordinaire new. *

Cependant, il a encore des utilisations pour des objets spéciaux dont la création est relativement onéreuse, comme les connexions DB / réseau, les threads, etc.

* Une fois, j'ai dû améliorer les performances d'une application Java d'analyse. L'enquête a révélé une tentative d'utilisation d'un pool d'objets pour allouer des millions d'objets ... et l'intelligent type qui l'a écrit a utilisé un verrou global unique pour le rendre thread-safe. Remplacer la piscine par uni a newrendu l'application 30 fois plus rapide.

Péter Török
la source
1
Alors, comment peut-on décider si l'instanciation d'un objet est trop chère?
user10326
3
Si l'objet utilise les ressources du système d'exploitation (unités d'exécution, E / S, mémoire partagée, etc.)
kevin cline
13
@ user10326, par mesure :-) Si la création de vos objets prend du temps, et / ou s'ils sont associés à des ressources spéciales, non limitées en mémoire, potentielles, vous pouvez envisager une mise en pool.
Péter Török
8
@ user10326, IMO dans plus de 95% des cas, les critères ci-dessus vous permettent de décider facilement à l'avance si vous avez besoin d'un pool d'objets. (En outre, dans presque tous les cas nécessitant un pool, vous utiliserez probablement une bibliothèque / un framework existant, sur lequel le pool d'objets a déjà été implémenté pour vous.) Pour le reste, il est toujours facile de masquer la création d'objets, par exemple. une usine, qui peut être ultérieurement réimplémentée comme bon vous semble.
Péter Török
2
Point très important soulevé par @ Peter Torok: de nombreux frameworks et bibliothèques implémentent le pooling pour vous, assurez-vous TOUJOURS que vous n'utilisez pas déjà une bibliothèque en pool avant de mettre en place la vôtre.
Hromanko
36

La réponse à la question concrète: "La mise en commun d'objets est-elle une technique obsolète?" est:

Le regroupement d'objets est largement utilisé dans des lieux spécifiques - regroupement de threads, regroupement de connexions de bases de données, etc.

La création générale d'objets n'a jamais été un processus lent. Le regroupement en soi consomme des ressources - mémoire et puissance de traitement. Toute optimisation est un compromis.

La règle est la suivante:

L'optimisation prématurée est mauvaise !!!

Mais quand une optimisation donnée est-elle prématurée?

Une optimisation prématurée est une optimisation effectuée avant d'avoir découvert un goulot d'étranglement via un profilage approfondi .

Boris Yankov
la source
2
En effet. OP a déclaré "J'essaie toujours de l'utiliser autant que possible" - tel est le problème, OMI.
nerdytenor
@ Boris, Donc, selon votre deuxième phrase, nous ne devrions pas objecter les connexions et les threads de la base de données du pool tant que nous ne les avons pas découverts comme un goulot d'étranglement via le profilage?
Pacerier
1
@Pac Certains résultats de profilage n'ont pas besoin d'être constamment redimensionnés :-)
David Bullock
9

Dans les situations où vous souhaitez éviter complètement le ramassage des ordures, je pense que le regroupement d'objets est la seule alternative viable. Donc non, ce n'est absolument pas une technique obsolète.

Jer
la source
1
Et j'ajouterais que c'est une bonne idée d'éviter la GC lorsque les objets ont une durée de vie suffisante pour être déplacés dans l'ancienne génération.
Zan Lynx
8

Mesure

Cela dépend complètement de votre cas d'utilisation, de la taille de vos objets, de votre machine virtuelle Java, de vos options de machine virtuelle, de ce que vous avez activé dans le GC et d'une foule d'autres facteurs.

En bref: mesurez-le avant et mesurez-le après. En supposant que vous utilisiez un framework de pool d’objets (comme celui d’Apache), il ne devrait pas être trop pénible d’échanger les implémentations.

Conseil de test de performance supplémentaire - laissez la JVM se réchauffer un peu, exécutez les tests plusieurs fois sur une JVM en cours d’exécution, elle peut se comporter différemment.

Martijn Verburg
la source
3
"Laissez la machine virtuelle se réchauffer un peu en premier" - Je me souviens du moment où la seule chose à "réchauffer" était le moniteur. Oy, tout ce qui est nouveau est vieux à nouveau.
Kylben
La seule chose dont j'ai besoin pour me réchauffer, c'est le café!
Désillusionné
@ Marijn, comment laissez-vous "se réchauffer"?
Pacerier
Voir le Framework JMH pour une explication complète ( openjdk.java.net/projects/code-tools/jmh ), mais vous devez fondamentalement donner à la JVM une chance de récupérer votre code, exécuter les GC avant votre référence, etc.
Martijn Verburg
8

Ce regroupement nuit en fait aux performances des programmes, en particulier dans les applications concurrentes, et il est conseillé d'instancier de nouveaux objets, car dans les nouvelles machines virtuelles, l'instanciation d'un objet est très rapide.

Dépend du contexte.


la source
1
Excellente réponse et convaincante. J'ajouterais (peut-être peut-être sous forme d'astérisque?) Que la revendication "24 octets" fait référence à 4 instances de flottants de 4 octets (16 octets), plus 4 octets pour la référence d'objet, plus 4 octets pour une référence de verrou. C'est la surcharge exacte que votre conception élimine.
Strickli
5

Je ne sais pas s'il y a une tendance changeante ici, mais ça va certainement être le cas, ça dépend . Si votre classe Java gère une ressource externe, telle qu'une connexion RMI ou le chargement d'un fichier de ressources, etc., le coût de l'instanciation d'objet peut toujours être élevé (même si ces ressources peuvent déjà être mises en pool pour vous!). En règle générale, je suis d'accord avec le livre.

Jeremy
la source
Eh bien maintenant, je ne sais pas.Parce que même dans ce cas, vous décrivez lequel (avant de lire ceci) j'utiliserais définitivement le pooling, j'aurais aussi un overhead.1) Nouvelles constructions pour gérer le pooling 2) Synchronisation de l'objet de réception / libération du pool 3) maintenant le pool, etc. Donc, je pense maintenant qu’il n’ya peut-être aucun cas d’utilité, sauf par exemple la mise en cache d’une socket au lieu d’en ouvrir une nouvelle à chaque fois pour se connecter au serveur.Et ce cas est dû au réseau temps d'attente pour la création de latence et non d'instanciation
user10326,
@ user10326 Oui exactement. Je vois l'ouverture d'un socket comme une partie de la surcharge d'instanciation. Si c'est le travail de la classe de le faire et qu'il doit être initialisé dans le constructeur, les impacts sur la latence et les entrées / sorties vous intéressent.
Jeremy