IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Java : le projet Valhalla validé par Oracle
Pour préparer le terrain aux futures évolutions de la plateforme

Le , par Hinault Romaric

47PARTAGES

1  0 
Le projet Valhalla pour la plateforme Java prend forme. Annoncé il y a quelques mois par Brian Goetz, responsable du projet, celui-ci a été validé par Oracle.

Le projet Valhalla est une sorte d’incubateur pour le développement des futures fonctionnalités de la plateforme Java. Celui-ci va tenter d’améliorer le langage Java en l’affectant à différents niveaux, comme le typage, le langage, la machine virtuelle ou les bibliothèques. Le projet a pour vocation de définir une sémantique claire au niveau de la machine virtuelle pour permettre un support égal pour le langage Java et les autres langages recourant à la JVM.

La révision majeure dans la description actuelle du projet concerne les génériques. Les versions actuelles de Java ne permettent pas que les types génériques contiennent les types primitifs, et le compilateur Java supprime les détails du type contenu lors de la compilation. Cette approche de typage générique (effacement de type) représente l’un des aspects assez critiqués du système de types de Java.

Le projet Valhalla explorera une nouvelle approche pour la gestion des types génériques, et espère produire une nouvelle forme de typage générique, qui permettra aux développeurs d’appliquer des génériques aux types primitifs. Ainsi, dans les futures versions de Java, des déclarations telles que List<int> pourront être valides en Java.

D’autres modifications importantes sont également au menu comme les types valeurs, qui permettront une optimisation de la représentation mémoire des types tout en maintenant les mêmes performances. Les types valeurs auront pour objectif de combiner certaines propriétés des types primitifs et des types objets, pour donner naissance à un nouveau type, qui sera manipulé par le développeur comme un type primitif.

Les développeurs intéressés par le projet peuvent rejoindre sa liste de diffusion sur le site d’OpenJDK. Brian Goetz a souligné dans l’annonce que le projet est encore à un stade embryonnaire, donc la communauté ne devrait pas s’attendre à trouver une technologie développée au sein de Valhalla dans JDK 9, dont la version finale est prévue pour 2016.

Annonce du projet projet Valhalla

Description du projet Valhalla

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de tomlev
Rédacteur/Modérateur https://www.developpez.com
Le 29/07/2014 à 17:07
Citation Envoyé par super_navide Voir le message
reste plus qu'a pouvoir surcharger les opérateurs comme + - / etc..
Bah dans ce cas tu peux faire du C#, qui fait tout ce que fait Java, a des types valeur et permet la surcharge d'opérateurs

(bah oui, je prêche pour ma paroisse, hein )
4  0 
Avatar de adiGuba
Expert éminent sénior https://www.developpez.com
Le 31/12/2014 à 12:29
Citation Envoyé par thelvin Voir le message
Hmmm. Bon ça a l'air de marcher. Ça va encore démultiplier les classes à charger en mémoire mais ça on n'y aurait pas coupé à un moment ou à un autre.
C'est atténué par le fait que cela ne concernera que les primitifs/valeurs.
Les Generics avec des références fonctionneront toujours de la même manière.

Citation Envoyé par thelvin Voir le message
Ça va tout de même commencer à devenir compliqué, la lecture des JavaDoc des classes standard Java : "alors les méthodes remove(Object) et remove(int) elles existent que sur les instances où le E de List<E> est un type référence. Pour les types primitifs ça existe pas, utilisez removeItem(E) et removeIndex(int)"
Dans l'idée removeItem(E) et removeIndex(int) seront disponible pour tous les types, et les remove(Object)/remove(int) accessible uniquement pour les références pour la rétrocompatibilité.

Depuis Java 8 la javadoc inclut des onglets pour regrouper les méthodes selon plusieurs critères (instance, static, abstraite, concrète, default...).
On aura peut-être de nouveaux onglets pour gérer cela...

Mieux : c'est absent de ce document, mais dans la version précédente ils parlaient carrément de pouvoir faire une implémentation spécifique pour certains types.
Il serait ainsi possible par exemple d'avoir une implémentation d'ArrayList<boolean> basé sur un BitSet au lieu d'un boolean[]

Citation Envoyé par super_navide Voir le message
Je comprend pas bien ce genre d'évolution qui n'apporte pas grand chose.
C'est un prérequis à l'intégration des types valeurs.
Il serait absurde d’insérer des types valeurs dans le langage si on ne peut pas les utiliser avec les Generics.

Citation Envoyé par super_navide Voir le message
Il ferait mieux de faire en sorte de faire évoluer java pour avoir des performance équivalente a du C++.
Troll ? Ou alors tu es resté bloqué sur la fin des années 90 ?
Un peu de sérieux voyons...

Citation Envoyé par super_navide Voir le message
La nouvelle fonctionnalité values prévu je sais pas dans quelle version était très intérréssante.
Ben c'est le même projet : tout ceci est lié.
Sans specialization l'utilisation des types valeurs risque d'être problématique...

Citation Envoyé par super_navide Voir le message
Sinon une fonctionnalité a embarquer dans JDK serait de pouvoir produire un executable qui ne néssécité pas d'avoir un JRE d'installer sur le PC.
Cela existe déjà (mais pas dans le JDK en effet).
Par contre je n'y vois pas grand intérêt...

Citation Envoyé par super_navide Voir le message
Je pense java souffre d'un seul problème ne pas être totalement géré comme python ou C++ pas communauté indépendante.
Je ne comprend pas trop.
Même si c'est chapeauté par Oracle l'évolution de Java est entre les mains de plusieurs entreprises/organisations...

Citation Envoyé par redcurve Voir le message
C'est vraiment n'importe quoi cette techno
Soit tu argumentes, soit c'est du troll pure et le mieux serait d'aller voir ailleurs.
Ici on préfère les discussions argumentés aux petites phrases toutes faites qui ne veulent rien dire...

Citation Envoyé par gstratege Voir le message
Ça sert à quoi d'avoir des types primitifs ?
Les types primitifs sont des types de base sans notion d'identité. Ils ne représente qu'un espace mémoire réservé à la valeur qu'ils représente.
Les types valeurs sont des types primitifs un peu plus évolué, dans le sens où ils peuvent être composé de plusieurs éléments (un peu comme une structure en C).

Leurs utilités en Java est restreinte, car en les utilisant on "perd" plein de fonctionnalité (pas d'héritage).

Toutefois cela a deux gros intérêts :
  • Une occupation mémoire fixe, qui permet des allocations en bloc pour les tableaux.
    Un tableau de 10 int occupera toujours la même taille en mémoire quelque soit les valeurs stockées, tandis qu'un tableau de 10 Object occupera plus ou moins de mémoire selon les objets qu'on y met.
  • Elle ne comporte que des données, et aucune informations d'identité propre à l'héritage et la POO.
    Cela permet d'être "partagé" plus facilement avec du code natif, ou des instructions bas-niveau, sans avoir à faire des conversions dans tous les sens.


C'est important lorsqu'on fait des traitements 3D ou d'autres choses qui peuvent être manipulé directement par le GPU : on génère les données en Java et on les passe tel-quel pour un traitements optimisés (voir même parallélisés).

Citation Envoyé par tomlev Voir le message
En tous cas c'est une très bonne chose qu'ils se penchent enfin sur l'amélioration des génériques; c'est vraiment la feature la plus bancale de Java... Le design actuel partait d'un bon sentiment (garder la compatibilité avec le code déjà compilé existant), mais ça introduit tellement de limitations qu'à mon avis les inconvénients surpassent largement les bénéfices.
Ils ne vont pas tout changer : les Generics fonctionneront quasiment de la même manière pour les objets.
Ils vont juste introduire une système de template (un peu à la C#), mais uniquement pour les primitives/valeurs.

Sinon perso je ne trouve pas que l'implémentation des Generics soit aussi mauvaise que tout le monde le laisse entendre.
Au contraire je trouve que ses inconvénients sont largement plus décriés qu'ils ne le devraient, et qu'ils y a d'autres avantages qui sont mis sous silence :
  • La compatibilité avec le code déjà compilé comme tu le dis,
  • Mais également l’inter-compatibilité entre du code n'utilisant pas les Generics, et du code les utilisant, au sein d'un même programme sans avoir à faire des conversions.
  • Une covariance/contravariance plus complète, et pas limité à la déclaration du type, et cela dès le début.
  • Une compatibilité avec tous les langages tournant sur une JVM, même si ceux-ci n'ont pas forcément de notion de Generics.


a++
4  0 
Avatar de rt15
Membre éclairé https://www.developpez.com
Le 04/09/2014 à 11:17
Citation Envoyé par wikipedia
Unlike garbage collection, ARC does not handle reference cycles automatically; it is up to the programmer to break cycles using weak references.
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.

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.
3  0 
Avatar de professeur shadoko
Membre chevronné https://www.developpez.com
Le 30/07/2014 à 10:27
Citation Envoyé par super_navide Voir le message

reste plus qu'a pouvoir surcharger les opérateurs comme + - / etc..
la surcharge d'opérateur a été refusée par l'équipe initiale de java pour éviter une confusion à la lecture du code.
Un des points fondamentaux de la philosophie initiale était: ce qui est compilé est ce qui est explicitement écrit ... malheureusement des évolutions ultérieures ont un peu contourné cette règle
mais la surcharge d'opérateur rend la lecture des codes C++ incroyablement difficile , et les programmeurs en abusent en violant allégrement la sémantique de l'opérateur.... vade retro satanas ...
(quoiqu'en Scala il y a un point de vue plus amusant sur la question, voir aussi en Groovy ... mais en Java j'espère que cette demande n'aboutira jamais )
edit: C# c'est la philosophie Microsoft: vous en voulez? alors en voilà! Peu importe si ça abouti à des formalismes peu en accord avec les règles du génie Logiciel (ou à des incohérences: je pense ici à la vision Microsoft de SQL qui donne un sens à des expressions illégales parce que les utilisateurs font souvent l'erreur ... alors ils donnent un sens à ce qui n'en a pas!)
2  0 
Avatar de M_Makia
Membre averti https://www.developpez.com
Le 31/07/2014 à 7:31
C'est une très bonne nouvelle.
La langage Java accuse d'un retard non négligeable par rapport à d'autres langages (Scala, Python, C# ...).
Il est temps de dépoussiérer un peu tout ça ^^
2  0 
Avatar de adiGuba
Expert éminent sénior https://www.developpez.com
Le 04/09/2014 à 11:25
Citation Envoyé par Gugelhupf  Voir le message
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++.

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
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 :
Code : Sélectionner tout
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
L'objet pointé par p2 n'est pas libéré car on n'appelle pas explicitement delete.
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++
2  0 
Avatar de Thyxx
Membre régulier https://www.developpez.com
Le 29/07/2014 à 17:03
Citation Envoyé par super_navide Voir le message
On peut dire adieu au C++

Mais tout à fait...
2  1 
Avatar de adiGuba
Expert éminent sénior https://www.developpez.com
Le 30/07/2014 à 17:55
Citation Envoyé par Gugelhupf Voir le message
J'ai lu la JEP 193 concernant l'évolution de "volatile" mais je n'ai pas tout à fait saisi l'objectif de ".volatile".
Non.

L'idée c'est d'avoir le même mécanisme que les classes AtomicInteger, etc. qui n'ont pas recours à la synchronisation.

Il faut savoir que les seules opérations atomiques c'est l'affectation d'une valeur :
Code : Sélectionner tout
1
2
3
count = 15;
// ou 
int temp = count;


Mais dès qu'il y a un calcul au milieu on n'est plus atomique, par exemple une incrémentation :
Code : Sélectionner tout
count += 1; // ou count++
En effet cela se décompose en fait en plusieurs instructions, que l'on pourrait représenter comme cela (en vrai c'est du bytecode, mais pour l'exemple je laisse en java) :
Code : Sélectionner tout
1
2
3
int next = count;
next++;
count = next;
Le problème c'est que si un autre thread modifie "count" entre ces deux opérations on se retrouve avec une valeur incorrect.

Par exemple si tu as deux threads qui incrément de 1 en même temps tu peux avoir cela :
Code : Sélectionner tout
1
2
3
4
int count = 0; // etat initial

count += 1; // thread 1
count += 1; // thread 2
On pourrait penser que le résultat sera forcément "2", mais ce n'est pas garantie.
En effet en décomposant on a en fait ceci :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
int count = 0; // etat initial

int next1 = count; // thread 1 (count vaut 0)
next1++; // thread 1
count = next1; // thread 1

int next2 = count; // thread 2  (count vaut 1)
next2++;  // thread 2
count = next2; // thread 2
Sauf que l'ordonnanceur peut nous jouer des tours, et on obtient alors une exécution qui ressemble à quelque chose comme cela :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
int count = 0; // etat initial

int next1 = count; // thread 1 (count vaut 0)
int next2 = count; // thread 2 (count vaut 0)

next1++; // thread 1
next2++; // thread 2

count = next1; // thread 1
count = next2; // thread 2
Du coup next1 et next2 valent tous les deux "1", et count prend finalement la valeur "1" alors qu'on l'a incrémenté deux fois !

En clair entre le moment ou on fait notre calcul et le moment ou on affecte le résultat dans la variable, la variable a été modifié par un autre thread
Du coup on devrait avoir un résultat différent, et notre calcul fait "disparaitre" la modif précédente...

En fait les classes AtomicXXX se base sur le compareAndSet, qui permet d'affecter une variable seulement si sa valeur correspond bien à celle indiqué.
Du coup l'incrémentation correspond plutôt à ceci :
Code : Sélectionner tout
1
2
3
int old = count;
int next = old + 1;
count.volatile.compareAndSet(old, next); // ne marche QUE si la valeur de count == old
L'idée c'est donc de faire cela dans une boucle, jusqu'à ce que compareAndSet() fonctionne.
Ainsi si "count" a été modifié par un autre thread entre temps, on reprend le calcul avec la valeur modifié et on réessaye.

C'est grosso-modo ce que font les méthodes d'AtomicInteger.
Cela peut paraitre curieux, mais c'est plus efficace que la synchronisation (cela n'a quasiment aucun "coût" lorsque la variable n'est pas modifié entre temps).
C'est même plus sûr car il n'y a pas forcément besoin d'utiliser synchronized sur toutes les utilisations de la variable...

L'objectif de la JEP c'est de permettre cela directement sur un attribut primitif, sans pour autant passer par les classes AtomicXXX...

a++
1  0 
Avatar de adiGuba
Expert éminent sénior https://www.developpez.com
Le 31/07/2014 à 12:53
@Gugelhupf : Attention c'est l'ancienne valeur qui est vérifiée, pas celle que l'on a calculé.
  • get() renvoit la valeur actuelle de l'AtomicInteger, que l'on stocke dans une variable locale ("current" dans l'exemple d'incrementAndGet()).
  • On fait notre calcul, que l'on stocke dans une autre variable ("next" dans l'exemple)
  • Enfin compareAndSet() n'affectera la valeur que l'on a calculer ("next" que si la valeur de l'AtomicInteger est toujours égale à "current".
    En clair si la valeur de l'AtomicInteger n'a pas été modifiée depuis la première fois qu'on y a accédé, on affecte notre résultat, sinon on recommence notre calcul car il n'est plus bon...


Citation Envoyé par thelvin Voir le message
Théorique maximum, c'est bel et bien l'infini. Si un autre thread passe son temps à incrémenter le compteur et que le thread en cours est bizarrement aligné par l'ordonnanceur de sorte de ne jamais, jamais faire une boucle entière avant que l'autre ne fasse la sienne, eh ben, il continuera d'essayer pour toujours et restera coincé là.
Oui.

Mais cela poserait également problème avec une synchronisation classique, puisqu'on serait bloqué sur l'acquisition du lock.
Dans ce cas l'utilisation de l'AtomicXXX serait plus gourmande en CPU (boucle active) tandis que le code synchronisé serait en sommeil.
Mais comme tu le dis il faut avoir une boucle infini qui modifie constamment la variable...

Dans un cas "normal" même si l'AtomicXXX doit faire quelques tours de boucles il y a des chances que ce soit plus rapide qu'une synchronisation....

a++
1  0 
Avatar de tomlev
Rédacteur/Modérateur https://www.developpez.com
Le 05/08/2014 à 13:50
Citation Envoyé par thelvin Voir le message
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...
1  0