En tout cas cela peut probablement grandement améliorer les perfs du java.
La représentation mémoire de certaines structures était certainement un vaste gaspillage.
Par exemple, a l'heure actuelle, une liste d'une centaine d'instances de points :
devait être stockée sous la forme d'une centaine d'objets (Les objets techniques de la liste dont un tableau, plus les 100 points). Car en mémoire, on avait globalement un tableau de référence vers des objets points.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 public class Point { private int x; private int y; }
A l'avenir, on peut espérer avoir que quelques objets, et un vrai tableau de points (Le tableau de références devient un tableau des instances de points).
Les gains, tant sur la mémoire que sur sur la consommation CPU peuvent être très importants. Gains sur la CPU car on évite des centaines d'allocations couteuses même en java, mais aussi sur les traitements telles que les parcours qui vont être plus rapides.
Par contre on risque des coûts de copies si on ne fait pas attention bien sûr : quand on trie par exemple, on risque de ne plus déplacer juste les références mais les instances qui peuvent être plus grosses.
Donc oui, java rattraperait un gros point faible si ce n'est le plus gros qu'il traine depuis longtemps.
Ouais enfin bon... Moi, des listes d'objets si grandes que l'empreinte mémoire s'en ressent, j'en ai jamais. J'ai des structures plus complexes, qui contiennent de grandes quantités de données et pèsent sur la mémoire, mais ces données ne sont pas simples au point de pouvoir être remplacées par un système non polymorphe.
Enfin, on ferait un mix des données qui peuvent en être et des données qui peuvent pas, si vraiment on avait besoin de réduire l'emprunte mémoire au lieu de juste gérer le fait qu'il y a besoin de beaucoup de mémoire. Mais on pourrait très bien le faire avec un décorateur d'accès à un ByteBuffer par exemple. C'est plus compliqué qu'avec des value types, ouais enfin... Pas tant que ça ! Parce que les value types ne font que le dixième du boulot. Les stocker de manière efficace et y accéder efficacement, c'est là la vraie difficulté, maintenant que c'est plus des objets.
Je la trouve pas franchement omniprésente, cette faiblesse.
Ce n'est pas seulement une question de mémoire, mais aussi de performance. Si on reprend l'exemple de rt15, sa centaine de points ne sont pas nécessairement contigus en mémoire ; il peuvent même se trouver dans des pages différentes de la mémoire, ce qui peut causer des chargements de page quand on passe d'un objet à l'autre, ce qui dégrade les perfs. Avec un tableau d'objets de type valeur, tu as tous les objets les uns à côté des autre en mémoire, et donc ça va plus vite...
EDIT: et c'est aussi moins de boulot pour le garbage collector...
Hummm...
Ouais, pas faux.
Enfin, moins de boulot pour le garbage collector ça veut juste dire que c'est pas lui qui fait le boulot. Si c'est pas lui, c'est nous. Dans la mesure où le boulot en question ne se fait pas au même moment dans les deux cas, c'est sûr que ça peut être très avantageux. Mais je me méfie toujours quand on me propose de confier moins de travail à l'ordinateur.
Même pas ! Il n'y a pas besoin de collecter les objets de type valeur, car ils n'ont pas leur propre emplacement sur le tas :
- soit ce sont des variables locales ou des paramètres, dans ce cas ils sont stockés sur la pile (donc rien à faire pour le GC)
- soit ce sont des membres d'un objet de type référence, et dans ce cas ils occupent une partie de la zone mémoire (sur le tas) de l'objet qui les contient (en l'occurrence le tableau)
Quand le GC collecte le tableau, la mémoire utilisée par les objets Point est collectée avec ; ça fait donc un seul objet à collecter au lieu de 100...
(tout ce que je dis là est valable pour .NET, mais ça m'étonnerait que Java fasse les choses très différemment s'ils implémentent les types valeur)
Quel est ce point de vue plus amusant en Scala?
Ceci? http://www.scala-graph.org/guides/co...tializing.html
Je plaisante, si j'étais prof je crois que j'imprimerai cette doc sur du papier rouge et je l'encadrerai pour montrer aux élèves où ça peut conduire d'avoir aucune limite à sa créativité. J'arrive pas à croire qu'on puisse écrire une librairie avec autant de ****** de symboles et la destiner à un autre être humain .
Enfin pour en venir à mon vrai point sur la question, c'est de savoir si une feature doit ou doit pas faire partie d'un langage sous peine qu'elle soit mal utilisée, ou abusée. Je pense qu'à un moment donné j'étais persuadé que le plus était le mieux et que les gens n'avaient qu'à faire attention, autant depuis que j'ai vu scala (enfin même avant en fait mais mes anticorps ont surtout commencé à grossir avec scala) j'ai l'impression que tout laisser faire est une mauvaise idée.
Même si on a une avalanche de possibilités avec les features d'un langage, on peut toujours écrire du code clean, y'a des gens qui savent écrire du bon Perl, du bon scala, du bon C++, du bon java, bref. Le problème c'est qu'on va forcément à un moment ou à un autre avoir besoin de lire du code qu'on a pas écrit, c'est rare de créer une application seul et sans librairies de nos jours. Et là plus le langage permet d'écrire n'importe quoi, plus on risque de se retrouver à lire du n'importe quoi. Et on en passe du temps à lire du code dans ce métier....
Le gros intérêt c'est surtout pour les tableaux et pour communiquer avec le natif (CPU ou code natif) en manipulant des types un peu plus complexe que les types primitifs de base.
Parce qu'en ce qui concerne l'allocation sur la pilele tas, c'est déjà le cas de certains objets grace à l'escape analysis (actif depuis Java 6).
En gros si la JVM détecte qu'un objet de taille restreinte reste confiné à l'intérieur de la méthode, elle peut l'allouer dans la pilele tasplutôt que dans le tasla pile.
Le fonctionnement devrait être similaire.
Par contre pour l'instant contrairement à C# cela ne se fait pas en définissant une structure mais plutôt une sorte de classe immuable.
a++
Tu veux dire l'inverse: tu peux laisser sur la pile un petit objet dont la portée n'excède pas la méthode. Certainement intéressant pour diminuer la pression sur le GC.
Par ailleurs il me semble que la façon dont l'allocation sur le tas est faite en java ou en .Net est différente de ce qu'on voit en C++ avec l'opérateur new(). Les implications au niveau performance ne sont pas forcément les même qu'un vrai malloc.
Je ne crois pas qu'il y ait déjà eu de gros débats là-dessus, que pensez vous de ARC (Automatic Reference Counting) ? Ne pensez-vous pas que cela enlèverait le coté aléatoire du Garbage Collector ? Pensez-vous qu'un tel système pourrait facilement être implémenté dans Java ?
PS: Je ne sais pas si c'est la news concernant le modèle de mémoire Java :
Ou bien les langages d'Apple qui me font le plus réfléchir à ce systèmeLes propositions faites concernent principalement le modèle mémoire. Elle voudrait le démêler et l’adapter à celui du C11 et C++11
L'ARC a un gros problème, c'est que cela ne peut pas gérer les références cycliques, et il faut gérer cela manuellement avec des weak-references...
Mais cela complique pas mal de chose pour le développeur !
Et du coup il serait impossible de remplacer le GC par de l'ARC car cela engendrerai beaucoup de fuites de mémoire...
a++
Mais s'il y a beaucoup de fuites de mémoire, pourquoi le C++ (avec les smart pointers), et les langages d'Apple (Objective-C & Swift) utilisent l'ARC ?
Parce que c'est toujours mieux que rien
Mais un ARC permet moins de chose qu'un GC, et il faut utiliser des pointeurs weak pour gérer les cas de références circulaires "à-la-main".
Donc si tu remplaces un GC par de l'ARC, tu engendres plein de fuites de mémoires sur toutes les références circulaires...
a++
Je ne pense pas que les ingénieurs travaillant sur le C++ ou ceux d'Apple mettraient en place ce système s'il y avait autant de fuite de mémoire. C'est d'ailleurs pour éviter les fuites de mémoire que les smart pointers existent en C++.
Traduction = Contrairement au GC, l'ARC ne gère par les cycles de référence automatiquement, c'est au programmeur de casser les cycles avec des références faibles.Envoyé par wikipedia
Donc oui, les ingés d'apple travail sur un système pouvant provoquer des fuites mémoires.
Ils veulent simplifier la vie des devs en passant d'une gestion manuelle de la mémoire à une gestion automatique (Même si pas infaillible).
Et du même coup, l'ARC est difficilement applicable directement au java où les programmeurs ne font pas (ou très peu) attention aux références circulaires car le GC est supposé s'en occupé ce qu'il fait pas trop mal de nos jours.
Les programmes existant sont souvent bourrés de références circulaires.
Et pourtant c'est bien le cas !!! Les références cycliques ne sont pas détecté.
Les smart pointers facilitent énormément la gestion des pointeurs, car tu peux les partager sans trop de difficulté (et sans trop te soucier de la libération) et tu n'as pas à les libérer explicitement.
Mais tout cela à condition qu'il n'y ait pas de référence circulaire.
Prend le code suivant, ou on affiche un message à la création/destruction de chaque objet :
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 #include <iostream> #include <memory> using namespace std; class MyObject { private: const char* name; std::shared_ptr<MyObject> ptr; public: MyObject(const char* n) { name = n; cout << "create " << name << std::endl; } ~MyObject() { cout << "delete " << name << std::endl; } void setOther(std::shared_ptr<MyObject> p) { ptr = p; } }; int main() { // p1 et p2 sont alloué dynamiquement MyObject* p1 = new MyObject("object1"); MyObject* p2 = new MyObject("object2"); // p3 est alloué dynamiquement via un smartpointeur std::shared_ptr<MyObject> p3(new MyObject("object3")); // p4 et p5 est alloué dynamiquement via un smartpointeur // et on fait un lien monodirectionnel de p4 vers p5 std::shared_ptr<MyObject> p4(new MyObject("object4")); std::shared_ptr<MyObject> p5(new MyObject("object5")); p4->setOther(p5); // p6 et p7 est alloué dynamiquement via un smartpointeur // et on fait un lien bidirectionnel entre p6 et p7 std::shared_ptr<MyObject> p6(new MyObject("object6")); std::shared_ptr<MyObject> p7(new MyObject("object7")); p6->setOther(p7); p7->setOther(p6); cout << std::endl; delete p1; return 0; }
Lorsqu'on l'exécute cela donne ceci (l'ordre des :
L'objet pointé par p2 n'est pas libéré car on n'appelle pas explicitement delete.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 create object1 create object2 create object3 create object4 create object5 create object6 create object7 delete object1 delete object4 delete object5 delete object3
Les pointeurs p6 et p7 sont libéré et l'ARC décrémente leurs compteurs respectifs.
Toutefois ces compteurs indique qu'il y a toujours une référence qui traine (respectivement dans object6 et object7) et du coup ils ne peuvent pas être libéré.
Bref ils sont toujours référencé pour l'ARC, même si on ne peut plus y accéder.
Exemple C++ : http://ideone.com/fdVA3X
En Java on n'a pas de tel problème... par contre on ne peut pas prédire le moment où ces objets seront libéré et on doit donc forcer le GC si on veut le démontrer (mais c'est crade il ne faut pas faire ca dans une application).
Exemple Java : http://ideone.com/k9WN64
En fait pour pallier aux références circulaires avec les smart pointeurs, il faut utiliser des "weaks références" : http://ideone.com/V6Mied
Mais il faut bien prendre en compte que ces "weaks références" peuvent se retrouver à null sans prévenir...
Donc oui dans des langages comme Java ou C#, le fait de passer d'un GC à l'ARC seraient une grosse regression, et entrainerait la modification d'un bon paquet de code...
a++
Autrement dit, ce qui se dit depuis plusieurs messages, ce n'est pas que l'ARC va fatalement faire des fuites mémoires.
C'est qu'il en fera si on s'en sert sur des références cycliques. Donc il ne faut pas faire de référence cyclique avec.
Ce qui n'est pas le cas d'un GC, lui gère les références cycliques. Donc le code Java et C# ne se gênent pas pour en avoir, et si on passait à ARC, ça ferait des fuites mémoires.
Sans déconner les gens -_-°...
Edit : merdoum, j'avais pas vu que rt15 avait dit exactement ça -_-°
Je n'ai pas grand chose à dire... Merci pour ces explications et les exemples adiGuba
Java : une piste intéressante pour améliorer les types génériques
Un prototype de la « generic specialization » en cours de développement
Même si Java 9 n’est pas encore prêt, une équipe de développeurs est chargée de préparer les nouvelles fonctionnalités de la version 10. Annoncé en juillet dernier, le projet Valhalla, dirigé par Brian Goetz, a pour but d’étudier et tester ces nouvelles fonctionnalités dont la publication est prévue pour 2016.
Il y a quelques jours, Goetz publia un document où il présente l’état d’avancement concernant la gestion des types génériques, l’une des caractéristiques les plus critiquées du Java puisqu’il n’est pas possible, actuellement, d’appliquer des génériques aux types primitifs.
Étant donné qu’Oracle accorde une importance primordiale à la compatibilité avec les versions précédentes, le problème soulevé de par l’introduction d’un tel système de « génériques améliorés » doit être approché avec prudence. En effet, la difficulté est que le « système de types du langage Java n'a pas de racine unifiée », il n'y a pas de type en Java qui est à la fois un super-type d’« Object » et de « int ».
Comme l’explique Goetz, « l'un des premiers compromis avec les génériques en Java est que ces variables ne peuvent être instanciées qu’avec les types de référence, et non pas les types primitifs […] Nous voulons permettre aux classes génériques existantes d’être améliorées, sans avoir à les jeter et les remplacer par de nouvelles. Et en même temps ne pas forcer les programmes existants à être recompilés ou modifiés simplement pour continuer à travailler ».
Pour cela, plusieurs techniques potentielles sont en train d’être étudiées. L’une des pistes les plus prometteuses, appelée « generic specialization », consiste à continuer à représenter les types du genre List<Integer> et List<String> par List.class dans le runtime, tandis que les nouvelles déclarations du genre List<int> seront représentées par un autre type.
L’équipe du projet Valhalla est en train de préparer un prototype pour tester cette technique. Cependant, il est encore tôt pour savoir si elle permet de résoudre efficacement tous les problèmes actuels du typage générique de Java.
Source : Open JDK
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager