Faut-il éviter la création d'objets en Java?

243

Un collègue m'a dit que la création d'objets en Java est l'opération la plus chère que vous puissiez effectuer. Je ne peux donc que conclure à la création du moins d’objets possible.

Cela semble quelque peu contrecarrer le but de la programmation orientée objet. Si nous ne créons pas d'objets, nous n'écrivons qu'un seul style de classe C, pour l'optimisation?

Slamice
la source
39
"La création d'objet Java est l'opération la plus chère que vous puissiez effectuer" . L'esprit partage-t-il la source de cette revendication?
Songo
92
Et - opération la plus chère par rapport à quoi? Comparé au calcul de pi à 900 chiffres ou comparé à l’incrémentation d’un int?
jasonk
4
Ont-ils pu parler d'une partie particulière de la création de cet objet spécifique? Je pense à la façon dont Apple utilise une file d'attente pour tableViewCells. Peut-être votre collègue a-t-il suggéré de créer quelques objets et de les réutiliser en raison des frais généraux associés à ces objets spécifiques? J'essaie juste de comprendre pourquoi ils feraient une telle réclamation.
Tyler DeWitt
3
C’est l’une des choses les plus drôles que j’ai jamais entendu parler de programmation.)
Mert Akcakaya
22
L'arithmatique est assez cher aussi; nous devrions éviter les calculs, oh, et démarrer une JVM est vraiment coûteux, nous ne devrions donc pas démarrer de JVM :-).
James Anderson

Réponses:

478

Votre collègue n'a aucune idée de ce dont ils parlent.

Votre opération la plus chère serait de les écouter . Ils vous ont fait perdre votre temps à vous orienter vers des informations obsolètes depuis plus de dix ans (à la date à laquelle cette réponse a été publiée) et à passer du temps à poster ici et à rechercher Internet sur la vérité.

Espérons qu'ils ne font que régurgiter par ignorance quelque chose qu'ils ont entendu ou lu il y a plus de dix ans et qu'ils ne savent pas mieux. Je prendrais également pour suspect tout ce qu'ils ont dit, il devrait s'agir d'une erreur bien connue de quiconque se tient au courant de toute façon.

Tout est un objet (sauf primitives)

Tous les int, long, doubleobjets autres que les primitives ( , etc.) sont des objets en Java. Il n'y a aucun moyen d'éviter la création d'objets en Java.

La création d'objets en Java, en raison de ses stratégies d'allocation de mémoire, est plus rapide que le C ++ et, à toutes fins pratiques, par rapport à tout le reste de la machine virtuelle Java, elle peut être considérée comme "libre" .

Au début, comme à la fin des années 1990 et au début des années 2000, l’implémentation réelle d’objets a été légèrement ralentie par l’implémentation de JVM. Cela n’a pas été le cas depuis au moins 2005.

Si vous accordez la prise -Xmsen charge de toute la mémoire dont vous avez besoin pour que votre application fonctionne correctement, il se peut que le GC ne soit jamais obligé d'exécuter et de balayer la plupart des déchets dans les implémentations modernes du GC. Les programmes de courte durée peuvent ne jamais supporter la GC.

Il n'essaie pas d'optimiser l'espace libre, ce qui est un casse-tête de toute façon, il optimise les performances de l'exécution. Si cela signifie que la pile JVM est allouée presque à 100% tout le temps, qu’il en soit ainsi. De toute façon, la mémoire de pile JVM libre ne vous fournit rien.

Il y a une idée fausse que le GC va libérer la mémoire de manière utile au reste du système, c'est complètement faux!

Le segment de mémoire de la machine virtuelle Java ne grossit pas et ne diminue pas, de sorte que le reste du système est affecté positivement par la mémoire disponible dans le segment de mémoire de la machine virtuelle Java . -Xmsalloue TOUT ce qui est spécifié au démarrage et sa méthode heuristique consiste à ne jamais réellement restituer cette mémoire au système d'exploitation afin de la partager avec tout autre processus du système d'exploitation jusqu'à la fermeture complète de cette instance de la machine virtuelle Java. -Xms=1GB -Xmx=1GBalloue 1 Go de RAM quel que soit le nombre d'objets créés à un moment donné. Certains paramètres permettent de libérer des pourcentages de la mémoire de tas, mais pour des raisons pratiques, la JVM n'est jamais en mesure de libérer suffisamment de mémoire pour que cela se produise.ainsi, aucun autre processus ne peut récupérer cette mémoire, de sorte que le reste du système ne bénéficie pas non plus de la pile JVM. Un RFE pour cela a été "accepté" le 29-NOV-2006, mais rien n’a été fait à ce sujet. Ce comportement n’est considéré comme une préoccupation par personne d’autorité.

Il existe une idée fausse selon laquelle la création de nombreux objets de courte durée entraîne la pause de la machine virtuelle Java pendant de longues périodes. Cette erreur est également fausse.

Les algorithmes actuels du GC sont en fait optimisés pour la création de nombreux petits objets de courte durée. Il s’agit en fait de l’heuristique à 99% pour les objets Java dans chaque programme. Les tentatives de regroupement d’objets vont en réalité ralentir les performances de la machine virtuelle Java dans la plupart des cas.

Les seuls objets qui ont besoin d'être mis en pool aujourd'hui sont les objets qui font référence à des ressources finies externes à la machine virtuelle Java; Les sockets, fichiers, connexions à la base de données, etc. peuvent être réutilisés. Les objets normaux ne peuvent pas être regroupés dans le même sens que dans les langues vous permettant d'accéder directement aux emplacements de mémoire. La mise en cache des objets est un concept différent et peut être ou ne pas être ce que certaines personnes appellent naïvement le pooling . Les deux concepts ne sont pas la même chose et ne doivent pas être confondus.

Les algorithmes GC modernes ne rencontrent pas ce problème car ils ne désallouent pas de manière planifiée, ils le font quand la mémoire disponible est nécessaire pour une génération donnée. Si le tas est suffisamment grand, aucune désallocation ne se produit suffisamment longtemps pour provoquer des pauses.

Les langages dynamiques orientés objet battent le C même aujourd'hui plusieurs jours sur les tests sensibles au calcul.

Communauté
la source
275
+1: "Votre opération la plus chère serait de les écouter ...". Meilleur que j'ai entendu depuis un certain temps.
Rjnilsson
21
@DeadMG, même avec la surcharge cumulative du GC, Java peut être plus rapide que le C ++ (par exemple, en raison du compactage du tas, minimisant les erreurs de cache pour certaines structures de données).
SK-logic
13
@ SK-logic: Le GC n'étant pas déterministe, il est extrêmement difficile au mieux de le prouver. En ce qui concerne la réduction des erreurs de cache, il est difficile de faire cela lorsque chaque objet doit être un autre indirection, gaspillant de l'espace de cache et augmentant le temps d'exécution. Cependant, je soutiens qu'en utilisant simplement un allocateur approprié en C ++, tel qu'un pool d'objets ou une arène de mémoire, vous pouvez facilement égaler ou surpasser les performances du garbage collector. Par exemple, vous pouvez écrire une classe d'arène de mémoire dont les performances seront supérieures à celles _allocaamorties.
DeadMG
33
La création d'objets est moins chère aujourd'hui qu'elle ne l'était auparavant. Ce n'est pas gratuit cependant. Quiconque vous dit que c'est mentir. Et le lien entre les langues OO qui battent le C est une réponse exagérée à une personne qui essaie réellement d’apprendre.
jasonk
17
C'est à cause de ce genre de réponses que nous nous retrouvons avec un code de merde. La bonne réponse est de créer un objet en Java, à la fois créer l'objet Java et l'initialiser. La première partie est bon marché, la seconde peut être très chère. Les gens devraient toujours regarder ce qui se passe dans les constructeurs avant d'utiliser le newmot clé dans un hotspot. J'ai vu des gens utiliser new ImageIcon(Image)dans la paint()méthode des objets Swing, ce qui est assez coûteux et rendait l'interface utilisateur super lente. Donc, ce n'est pas une réponse en noir et blanc, réfléchissez avant d'utiliser newquelque part.
Qwertzguy
94

En bout de ligne: Ne compromettez pas votre conception afin de prendre des raccourcis créant des objets. Évitez de créer des objets inutilement. Si nécessaire, veillez à éviter les opérations redondantes (de quelque nature que ce soit).

Contrairement à la plupart des réponses - oui, l’allocation d’objets a un coût associé. Le coût est faible, mais vous devez éviter de créer des objets inutiles. Même chose que vous devriez éviter rien inutile dans votre code. Les graphiques d'objet volumineux ralentissent le CPG, impliquent des temps d'exécution plus longs, car vous allez probablement avoir plus d'appels de méthode, déclencher plus d'erreurs dans le cache du processeur et augmenter la probabilité que votre processus soit échangé sur le disque dans les cas où la RAM est faible.

Avant que quiconque ne prétende qu'il s'agisse d'un cas limite, j'ai profilé des applications qui, avant d'optimiser, ont créé plus de 20 Mo d'objets afin de traiter environ 50 lignes de données. Cela convient très bien dans les tests, jusqu'à ce que vous augmentiez jusqu'à cent demandes par minute et que vous créiez tout à coup 2 Go de données par minute. Si vous voulez faire 20 requêtes par seconde, vous créez 400 Mo d’objets, puis vous les jetez. 20 reqs / sec est minuscule pour un serveur décent.

jasonk
la source
7
J'ajouterai un exemple: dans certains cas, la clarté du code ne fait aucune différence, mais les performances peuvent être plus ou moins différentes: lors de la lecture de flux, par exemple, il n'est pas rare d'utiliser quelque chose comme while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...cela. être un gaspillage par exemple byte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ....
JimmyB
4
+1: La création inutile d'objets (ou plutôt le nettoyage après suppression) peut parfois être coûteuse. Le code 3D Graphics / OpenGL en java est l’un des endroits où j’ai vu les optimisations effectuées pour réduire le nombre d’objets créés, car GC pourrait sinon faire des ravages avec votre framerate.
Léo
1
Hou la la! 20+ Mo pour traiter 50 lignes de données? Cela semble fou. En tout cas, ces objets ont-ils une vie longue? Parce que c'est le cas qui importerait pour GC. Si, par contre, vous ne faites que discuter des besoins en mémoire , cela n'a aucun rapport avec l'efficacité de la récupération de place ou de la création d'objets ...
Andres F.
1
La durée de vie des objets est courte (moins de 0,5 seconde dans le cas général). Ce volume de déchets affecte toujours les performances.
jasonk
2
Merci de rester ferme sur la réalité, quiconque vit avec les effets d'une architecture irréfléchie peut très bien comprendre.
JM Becker
60

En fait, en raison des stratégies de gestion de la mémoire rendues possibles par le langage Java (ou tout autre langage géré), la création d’objets n’est guère plus que l’incrémentation d’un pointeur dans un bloc de mémoire appelé jeune génération. C'est beaucoup plus rapide que C, où une recherche de mémoire libre doit être faite.

L'autre partie du coût est la destruction d'objets, mais il est difficile de comparer avec C. Le coût d'une collection est basé sur la quantité d'objets sauvegardés à long terme, mais la fréquence des collections est basée sur la quantité d'objets créés ... dans Au final, il reste beaucoup plus rapide que la gestion de la mémoire de type C.

Amara
la source
7
+1 Même si le ramasse-miettes "fonctionne correctement", tout programmeur Java devrait en savoir plus sur le ramasse-miettes générationnel.
benzado
18
Les versions plus récentes de Java peuvent effectuer une analyse d'échappement, ce qui signifie qu'elle peut allouer de la mémoire aux objets n'échappant pas à une méthode de la pile, de sorte que leur nettoyage est gratuit - le garbage collector n'a pas à gérer ces objets. , ils sont automatiquement rejetés lorsque le cadre de pile de la méthode est déroulé (au retour de la méthode).
Jesper
4
Une étape suivante de l'analyse d'échappement consiste à "allouer" de la mémoire pour un petit objet uniquement dans des registres (par exemple, un Pointobjet peut tenir dans deux registres à usage général).
Joachim Sauer
6
@ Joachim Sauer: C'est un peu ce qui est fait dans les nouvelles implémentations de la machine virtuelle HotSpot. C'est ce qu'on appelle le remplacement scalaire.
Someguy
1
@someguy: J'ai lu quelque chose à ce sujet il y a quelque temps, mais je n'ai pas suivi pour vérifier si c'était déjà fait. C'est une excellente nouvelle d'entendre que nous avons déjà cela.
Joachim Sauer
38

D'autres afficheurs ont souligné à juste titre que la création d'objets est extrêmement rapide en Java et que vous ne devriez généralement pas vous en soucier dans les applications Java normales.

Il y a quelques situations très particulières où est est une bonne idée d'éviter la création d'objets.

  • Lorsque vous écrivez une application sensible à la latence et souhaitez éviter les pauses GC. Plus vous produisez d'objets, plus la GC est importante et plus les chances de faire une pause sont grandes. Cela pourrait être une considération valable pour les jeux, certaines applications multimédias, le contrôle robotique, le trading haute fréquence, etc. La solution consiste à pré-allouer dès le départ tous les objets / matrices dont vous avez besoin et à les réutiliser. Il existe des bibliothèques spécialisées dans ce type de capacités, par exemple Javolution . Mais si vous vous souciez vraiment de la faible latence, vous devriez utiliser C / C ++ / assembler plutôt que Java ou C # :-)
  • Éviter les primitives en boîte (double, entier, etc.) peut être une micro-optimisation très bénéfique dans certaines circonstances. Etant donné que les primitives non encadrées (double, int, etc.) évitent les frais généraux par objet, elles nécessitent beaucoup plus de temps processeur, comme le traitement numérique, etc. tout autre type d'objets.
  • Dans les situations de mémoire contrainte, vous souhaitez réduire le nombre d'objets (actifs) créés, car chaque objet est légèrement surchargé (généralement entre 8 et 16 octets, en fonction de l'implémentation de la machine virtuelle Java). Dans de telles circonstances, vous devriez préférer un petit nombre de grands objets ou de tableaux pour stocker vos données à un grand nombre de petits objets.
Mikera
la source
3
Il est intéressant de noter que l'analyse d'échappement permettra de stocker des objets à vie courte (vie limitée) sur la pile, ces objets peuvent être désalloués gratuitement ibm.com/developerworks/java/library/j-jtp09275/index.html
Richard Tingle
1
@ Richard: excellent point - l'analyse d'échappement fournit de très bonnes optimisations. Vous devez cependant faire attention à ne pas vous y fier: cela n’est pas garanti dans toutes les implémentations Java et dans toutes les circonstances. Donc, vous avez souvent besoin de points de repère pour être sûr.
Mikera
2
En outre, une analyse d'échappement correcte à 100% équivaut au problème d'arrêt. Ne vous fiez pas à l'analyse d'évasion dans des situations complexes, cela risquerait de donner la mauvaise réponse au moins de temps en temps.
Jules
Les premier et dernier points ne s'appliquent pas aux petits objets qui sont rapidement libérés, ils meurent in eden (pensez à l'allocation de pile en C, à peu près libre) et n'affectent jamais les durées de GC ni l'allocation de mémoire. La plupart des objets en Java appartiennent à cette catégorie et sont donc essentiellement gratuits. Le deuxième point est toujours valable.
Bill K
17

Ce que dit votre collègue contient un noyau de vérité. Je suggère respectueusement la question avec l' objet la création est en fait poubelle collection . En C ++, le programmeur peut contrôler avec précision la libération de la mémoire. Le programme peut accumuler crud aussi longtemps ou aussi peu qu’il le souhaite. De plus, le programme C ++ peut ignorer le crud en utilisant un autre thread que celui qui l'a créé. Ainsi, le thread qui travaille actuellement ne doit jamais s'arrêter pour nettoyer.

En revanche, la machine virtuelle Java (JVM) arrête périodiquement votre code pour récupérer la mémoire inutilisée. La plupart des développeurs Java ne remarquent jamais cette pause, car elle est généralement peu fréquente et très courte. Plus vous accumulez de crud ou plus votre JVM est contrainte, plus ces pauses sont fréquentes. Vous pouvez utiliser des outils tels que VisualVM pour visualiser ce processus.

Dans les versions récentes de Java, l'algorithme de récupération de place (GC) peut être optimisé . En règle générale, plus les pauses sont courtes, plus la surcharge de la machine virtuelle est onéreuse (c.-à-d. Que la CPU et la mémoire ont passé de temps à coordonner le processus du CPG).

Quand est-ce que cela pourrait avoir de l'importance? Chaque fois que vous vous souciez de taux de réponse cohérents inférieurs à une milliseconde, vous vous souciez de GC. Les systèmes de trading automatisés écrits en Java adaptent fortement la machine virtuelle pour minimiser les pauses. Les entreprises qui écriraient autrement Java se tournent vers C ++ dans des situations où les systèmes doivent être très réactifs en permanence.

Pour mémoire, je ne tolère pas l' évitement d'objet en général! Par défaut à la programmation orientée objet. Ajustez cette approche uniquement si le CPG se met en travers de votre chemin, et seulement après avoir tenté d’ajuster la machine virtuelle Java de manière à la suspendre moins longtemps. Java Performance de Charlie Hunt et Binu John est un bon livre sur le réglage des performances Java .

greg
la source
10
La plupart des machines virtuelles modernes prennent en charge de meilleurs algorithmes de récupération de place que "stop the world". Le temps amorti passé dans la collecte des ordures est souvent inférieur à celui passé à appeler explicitement malloc / free. La gestion de la mémoire C ++ est-elle également exécutée dans un thread séparé?
10
Les nouvelles versions Java d'Oracle peuvent effectuer une analyse d'échappement, ce qui signifie que les objets qui n'échappent pas à une méthode sont alloués sur la pile, ce qui permet de les nettoyer gratuitement. Ils sont automatiquement désalloués au retour de la méthode.
Jesper
2
@ ThorbjørnRavnAndersen: Vraiment? Voulez - vous dire vraiment Pause- moins GC, ou voulez - vous dire « généralement parallèles mais, parfois, un petit-bit pause » GC? Je ne comprends pas comment le GC ne peut jamais mettre en pause un programme, et pourtant déplacer des objets en même temps ... cela me semble littéralement impossible, ...
Mehrdad,
2
@ ThorbjørnRavnAndersen: Comment peut-il "arrêter le monde" mais ne jamais "mettre en pause la JVM"? Cela n'a aucun sens ...
Mehrdad,
2
@ ThorbjørnRavnAndersen: Non, vraiment pas; Je ne comprends tout simplement pas ce que vous dites. Soit le GC suspend parfois le programme, auquel cas il est - par définition - non "sans pause", soit il ne met jamais le programme en pause (par conséquent, il est "sans pause"), auquel cas je ne comprends pas comment correspond à votre déclaration "cela arrête le monde quand il ne peut pas suivre", car cela semblerait impliquer que le programme est en pause pendant le fonctionnement du CG. Souhaitez-vous expliquer ce qui se passe (est-ce une pause ou non?) Et comment cela est-il possible?
Mehrdad
11

Il y a un cas où vous pouvez être dissuadés de créer trop d'objets en Java à cause de la surcharge - conception optimisée pour la performance sur la plate-forme Android

Autre que cela, les réponses ci-dessus sont vraies.

Peter Kelly
la source
2
Lisez à nouveau la question. Il ne spécifie pas la machine virtuelle Java, y compris les balises. Il ne mentionne que Java.
Peter Kelly
9

le GC est réglé pour de nombreux objets éphémères

Cela dit, si vous pouvez réduire de manière triviale l'allocation d'objets, vous devriez

un exemple serait la construction d'une chaîne dans une boucle, la manière naïve serait

String str = "";
while(someCondition){
    //...
    str+= appendingString;
}

qui crée un nouvel Stringobjet sur chaque +=opération (plus un StringBuilderet le nouveau tableau de caractères sous-jacent)

vous pouvez facilement réécrire ceci en:

StringBuilder strB = new StringBuilder();
while(someCondition){
    //...
    strB.append(appendingString);
}
String str = strB.toString();

ce modèle (résultat immuable et une valeur intermédiaire mutable locale) peut être appliqué à d'autres choses aussi

mais à part cela, vous devriez utiliser un profileur pour trouver le véritable goulot d'étranglement au lieu de chasser les fantômes.

monstre à cliquet
la source
le plus grand avantage de cette StringBuilderapproche est la pré-taille de la StringBuildersorte qu'il ne doit réattribuer le tableau sous - jacent avec le StringBuilder(int)constructeur. Cela en fait une allocation unique plutôt que des 1+Nallocations.
2
@JarrodRoberson StringBuilder doublera au moins la capacité actuelle. En d'autres termes, la capacité augmentera de manière exponentielle pour seulement les allocations log (n)
Freaket Freak,
6

Joshua Bloch (un des créateurs de la plate-forme Java) a écrit dans son livre Effective Java en 2001:

Éviter de créer des objets en conservant votre propre pool d'objets est une mauvaise idée, à moins que les objets du pool ne soient extrêmement lourds. Un exemple prototypique d'objet justifiant un pool d'objets est une connexion à une base de données. Le coût d'établissement de la connexion est suffisamment élevé pour qu'il soit logique de réutiliser ces objets. En règle générale, toutefois, la maintenance de vos propres pools d'objets encombre votre code, augmente l'encombrement de la mémoire et nuit aux performances. Les implémentations modernes de JVM ont des récupérateurs de mémoire hautement optimisés qui surpassent facilement de tels pools d'objets sur des objets légers.

Anthony Ananich
la source
1
Même avec des éléments tels que la connexion réseau, l’important n’est pas de conserver les objets associés aux connexions attribués au GC, mais bien de conserver l’ensemble des connexions qui existent en dehors du monde géré par le GC. La mise en commun des objets eux-mêmes peut parfois être utile; la plupart d'entre elles proviennent de cas où une paire de références au même objet peut être sémantiquement équivalente à une paire de références d'objets identiques, mais distincts, tout en présentant certains avantages. Par exemple, l'égalité peut être vérifiée dans deux références à la même chaîne de cinquante mille caractères ...
supercat
2
... beaucoup plus rapidement que les références à deux chaînes distinctes mais identiques de cette longueur.
Supercat
5

Cela dépend vraiment de l'application spécifique, donc c'est vraiment difficile à dire en général. Cependant, je serais assez surpris que la création d'objet soit en réalité un goulot d'étranglement dans les performances d'une application. Même s'ils sont lents, les avantages de ce type de code l'emporteront probablement sur les performances (à moins que l'utilisateur ne le remarque réellement)

Dans tous les cas, vous ne devriez même pas vous inquiéter de ces problèmes tant que vous n’avez pas profilé votre code pour déterminer les goulets d’étranglement des performances réelles au lieu de deviner. Jusque-là, vous devriez faire tout ce qui est préférable pour la lisibilité du code, pas pour la performance.

Oleksi
la source
Dans les premières versions de Java, le nettoyage des objets mis au rebut entraînait des coûts importants. Les versions récentes de Java ont considérablement amélioré les options du GC. La création d’objets est donc rarement un problème. Habituellement, les systèmes Web sont limités par des appels d'E / S vers des systèmes externes, à moins que vous ne calculiez quelque chose de vraiment complexe sur le serveur d'applications lors du chargement de la page.
Greg
3

Je pense que votre collègue a dû dire du point de vue de la création d'objet inutile. Je veux dire si vous créez fréquemment le même objet, il est préférable de le partager. Même dans les cas où la création d'objet est complexe et nécessite plus de mémoire, vous pouvez cloner cet objet et éviter de créer ce processus de création d'objet complexe (mais cela dépend de vos besoins). Je pense que la déclaration "La création d'objet coûte cher" devrait être considérée dans son contexte.

En ce qui concerne les exigences en matière de mémoire JVM, attendez Java 8, vous n'avez même pas besoin de spécifier -Xmx, les paramètres de méta-espace prendront en charge les besoins en mémoire de la JVM et ceux-ci augmenteront automatiquement.

AKS
la source
Ce serait très constructif si les gens ajoutaient des commentaires tout en votant à la baisse. Après tout, nous sommes tous ici pour partager nos connaissances, et le simple fait de juger sans raison valable ne servira à rien.
AKS
1
Stack Exchange devrait disposer d’un mécanisme permettant d’informer les utilisateurs qui votent à la baisse de toute réponse, lorsque des commentaires sont postés sur cette réponse.
AKS
1

Créer une classe ne se résume pas à allouer de la mémoire. Il y a aussi l'initialisation, je ne sais pas pourquoi cette partie n'est pas couverte par toutes les réponses. Les classes normales contiennent des variables et effectuent une certaine forme d'initialisation, ce qui n'est pas gratuit. Selon la classe, la classe peut lire des fichiers ou effectuer un nombre différent d'opérations lentes.

Pensez donc simplement à ce que le constructeur de la classe fait avant de déterminer s'il est gratuit ou non.

Alex
la source
2
Une classe bien conçue ne devrait pas faire de gros travaux chez son constructeur, juste pour cette raison. Par exemple, votre classe de lecture de fichier peut réduire son coût d'instanciation en vérifiant uniquement que son fichier cible existe au démarrage et en différant toutes les opérations de fichier réelles jusqu'au premier appel de méthode nécessitant des données à partir du fichier.
GordonM
2
Si le coût supplémentaire est déplacé vers le 'if (firstTime)', recréer une classe dans une boucle coûte plus cher que de réutiliser la classe.
Alex
J'étais sur le point de publier une réponse similaire, et je pense que c'est la bonne réponse. Tellement de gens sont aveuglés par ces affirmations selon lesquelles la création d’objets Java est peu coûteux, ils ne se rendent pas compte que, souvent, les constructeurs ou l’initialisation ultérieure peuvent être loin d’être peu coûteux. Par exemple, la classe ImageIcon dans Swing, même si vous lui transmettez un objet Image préchargé, le constructeur est assez coûteux. Et je ne suis pas d’accord avec @GordonM, beaucoup de classes du JDK exécutent la plupart des init dans le constructeur, et je pense que cela rend le code plus léger, mieux conçu.
Qwertzguy
1

Le GC de Java est en fait très optimisé en termes de création rapide et volumineuse d'objets. D'après ce que je peux comprendre, ils utilisent un allocateur séquentiel (l'allocateur O (1) le plus rapide et le plus simple pour les requêtes de taille variable) pour ce type de "cycle rafale" dans un espace mémoire qu'ils appellent "espace Eden", et uniquement si les objets persistent. après un cycle de GC, ils sont déplacés vers un endroit où le GC peut les collecter un par un.

Cela dit, si vos besoins en performances deviennent suffisamment critiques (mesurés avec les exigences réelles des utilisateurs finaux), alors les objets sont vraiment surchargés, mais je n'y penserais pas tellement en termes de création / allocation. Cela concerne davantage la localité de référence et la taille supplémentaire de tous les objets en Java, nécessaires pour prendre en charge des concepts tels que la réflexion et la répartition dynamique (elle Floatest plus grande que float, souvent environ 4 fois plus grande sur 64 bits avec ses exigences d'alignement, et tableau de Floatn'est pas nécessairement garanti d'être stocké contigu de ce que je comprends).

L’une des choses les plus accrocheuses que j’ai jamais vues développées en Java et qui m’a fait considérer comme un gros concurrent dans mon domaine (VFX) était un traqueur de chemin standard multithread interactif (ne pas utiliser le cache irradiance ni le BDPT ou le MLS ou toute autre chose) sur le processeur fournit des aperçus en temps réel qui convergent assez rapidement vers une image sans bruit. J'ai travaillé avec des professionnels du C ++, consacrant leur carrière à de telles choses avec des profils fantaisistes à la main qui avaient des difficultés à le faire.

Mais j’ai scruté le code source et, bien qu’il utilise beaucoup d’objets à un coût dérisoire, les parties les plus critiques du traceur de chemin (le BVH, les triangles et les matériaux) ont très clairement et délibérément évité les objets au profit de grands réseaux de types primitifs ( généralement float[]et int[]), ce qui lui a fait utiliser beaucoup moins de mémoire et une localité spatiale garantie pour aller d’un floatélément du tableau à l’autre. Je ne pense pas que ce soit trop spéculatif de penser que, si l'auteur utilisait des types en boîte aimaitFloatlà-bas, sa performance aurait été très coûteuse. Mais nous parlons de la partie la plus critique de ce moteur, et je suis à peu près sûr, étant donné l’habileté du développeur de l’optimiser, qu’il a mesuré cela et appliqué cette optimisation très, très judicieusement, puisqu’il a utilisé avec bonheur des objets partout ailleurs avec coût insignifiant pour son impressionnant traceur de chemins en temps réel.

Un collègue m'a dit que la création d'objets en Java est l'opération la plus chère que vous puissiez effectuer. Je ne peux donc que conclure à la création du moins d’objets possible.

Même dans un domaine aussi critique que le mien en termes de performances, vous n'écrirez pas des produits efficaces si vous vous bloquez dans des endroits sans importance. J'affirmerais même que les domaines les plus critiques en termes de performances pourraient entraîner une demande de productivité encore plus grande, car nous avons besoin de tout le temps supplémentaire nécessaire pour régler ces points chauds qui importent vraiment en ne perdant pas autant de temps sur des tâches qui ne le sont pas. . Comme avec l'exemple de traçage de chemin ci-dessus, l'auteur a appliqué habilement et judicieusement de telles optimisations uniquement aux endroits qui importaient vraiment, et probablement après coup, après avoir mesuré, et utilisaient toujours avec bonheur des objets partout ailleurs.

Énergie de dragon
la source
1
Votre premier paragraphe est sur la bonne voie. Les espaces eden et young sont des collecteurs de copie qui ont env. 0 coût pour les objets morts. Je recommande fortement cette présentation . Sur une note de côté, vous vous rendez compte que cette question est de 2012, non?
JimmyJames
@ JimmyJames Je m'ennuie et j'aime bien passer au crible les questions, y compris les plus anciennes. J'espère que les gens ne s'occupent pas de ma nécromancie! :-D
Dragon Energy
@JimmyJames N'est-il plus vrai que les types en boîte ont des besoins en mémoire supplémentaires et qu'il n'est pas garanti qu'ils soient contigus dans un tableau? J'ai tendance à penser que les objets sont vraiment bon marché en Java, mais l'un des cas où ils pourraient être relativement coûteux est comme float[]vs Float[], où le traitement séquentiel d'un million d' objets anciens peut être relativement plus rapide que le second.
Dragon Energy
1
Quand j'ai commencé à poster ici, j'ai fait de même. Ne soyez pas surpris lorsque les réponses à de vieilles questions attirent très peu l’attention.
JimmyJames
1
Je suis à peu près sûr que les types en boîte doivent être supposés utiliser plus d'espace que les primitives. Il y a potentiellement des optimisations autour de cela, mais en général, je pense que c'est comme vous l'avez décrit. Gil Tene (de la présentation ci-dessus) a une autre discussion sur une proposition visant à ajouter un type spécial de collection qui fournirait certains des avantages des structs, tout en utilisant des objets. C'est une idée intéressante, je ne suis pas sûr du statut de la JSR. Le coût est en grande partie dû au temps, ce à quoi vous faites allusion. Si vous pouvez éviter de laisser vos objets "s'échapper", vous pouvez même les affecter à la pile et ne jamais toucher le tas.
JimmyJames
0

Comme certains l’ont dit, la création d’objets n’est pas très coûteuse en Java (mais je parie que la plupart des opérations simples comme l’ajout, etc.) ne doit pas être évitée de trop.

Cela dit, cela reste un coût, et vous pouvez parfois essayer de supprimer autant d'objets que possible. Mais seulement après que le profilage a montré que c'est un problème.

Voici une excellente présentation sur le sujet: https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf

rkj
la source
0

J'ai fait un microbenchmark rapide à ce sujet et j'ai fourni des sources complètes dans github. Ma conclusion est que la question de savoir si la création d'objets est coûteuse ou non n'est pas le problème, mais la création continue d'objets avec la notion que le GC s'occupera de tout, votre application déclenchera plus tôt le processus de GC. Le GC est un processus très coûteux et il est préférable de l’éviter autant que possible et de ne pas essayer de le pousser à s’engager.

Archimedes Trajano
la source
cela ne semble rien offrir de substantiel par rapport aux points soulevés et expliqués dans les 15 réponses précédentes. À peine un contenu digne de répondre à une question qui a été posée il y a plus de 2 ans et a obtenu une réponse très complète
Gnat