OVH Cloud OVH Cloud

Java JVM performances / garbage collector

20 réponses
Avatar
Lionel
Bonjour,

J'ai une appli web struts/hibernate qui tourne sur websphere5/oracle sous
winXP.
Elle a des besoins en mémoire assez gros pour certaines requetes: elles
remontent plusieurs milliers d'objets, (posés sur la HttpServletRequest).
Si je fais un appel à Runtime.freeMomory() avant et après, j'ai un delta de
-80Mo en moyenne (est-ce fiable comme indication ?).
Entre le lancement de la requete oracle, et le moment ou tous les objets ont
été chargés dans ma
servlet il se passe 5 à 6 secondes. (Utilisant hibernate, je ne vois pas
trop comment isoler le temps
de la requete sql)
Lors de l'affichage du tableau par la jsp, j'ai systématiquement un appel au
garbage collector identique à celui ci:

<AF[4]: Allocation Failure. need 770608 bytes, 68096 ms since last AF>
<AF[4]: managing allocation failure, action=1 (78473408/533723648)
(3145728/3145728)>
<GC(4): GC cycle started Wed Feb 09 12:03:47 2005
<GC(4): freed 337237824 bytes, 78% free (418856960/536869376), in 335 ms>
<GC(4): mark: 308 ms, sweep: 27 ms, compact: 0 ms>
<GC(4): refs: soft 0 (age >= 32), weak 0, final 54, phantom 0>
<AF[4]: completed in 336 ms>

Je suis en monoprocesseurr avec une JVM IBM 1.3.1, avec les
paramètres -verbose:gc -Xms512m -Xmx512m.
Je ne vois pas d'autres paramètres pour cette VM.

Lorsque mon appli est déployée sur le serveur (HP-UX 64bits biproc 750Mhz
RISC) les performances sont catastrophiques: tout est exactement 6 fois plus
lent que sur mon poste de dév pour la meme requete dans les meme conditions.
Le simple parcours de l'iterator est 2 fois plus lent sur le serveur
que sur mon poste (1.1 seconde contre 0.5sec)
Je n'ai pas accès au serveur pour savoir si c'est le garbage collector qui
ralentit autant l'application en occupant le cpu.
Le serveur utilise une Java HotSpot(TM) Server VM 1.3.1.09.

Comment expliquer un tel écart de perf alors que ce serveur est censé etre
nettement plus puissant sur le papier ?
Le GC et/ou la JVM peuvent elles etre responsables d'un tel écart ?

Note:
Ce serveur de test héberge une vingtaine d'applications, mais je n'ai aucune
possibilité de savoir ce qu'elle font ni qui les utilise en meme temps que
moi.
Je suppose que ce sont ces appli qui bouffent le cpu et ralentissent mon
appli, mais ne peux pas dépasser le stade de la supposition...
je cherche donc à optimiser mon appli et son paramétrage au mieux avant de
remettre en cause le serveur.
Actuellement, je ne vois pas comment faire mieux sur mon poste de dév.

Merci pour vos conseils et expériences similaires...

10 réponses

1 2
Avatar
Derek Sagan
De mémoire, HP-UX n'est pas forcement la meilleur archi pour
faire tourner du Java.

<mode_troll>
Je ne vois qu'une solution pour faire tourner toute ton
usine (WebSphere, hibernate, etc etc) c'est d'installer
un E10K ou E15K.
En faisant ce qu'il faut pour avoir assez de mémoire
et assez de noeuds CPU, a mon avis tu devrais retrouver
des perfs correctes.

Ou encore monter un cluster de 8 Bi Xeon ....

J'arrete la ca ira ;)


Mouarf, ce qu'il faut c'est tout porter en .Net et mettre ça sur un
cluster de 10 PC 32 procs sous Windows 2003 datacenter en activant
l'option "seamless optimize your code and enlarge your penis".
Avec ça même le code monothreadé tire parti d'une architecture multi
processeur avec un cluster intercontinental (les 10 PC 32 procs sont sur
3 continents pour parer au risque de tsunami)
(désolé)

</mode_troll>

en bref, bonne chance :)


Avatar
Lionel
Derek Sagan wrote:
Ce qui me donne un tableau de 6000 lignes sur 30 colonnes.
Certes paginé, mais tout de meme chargé en mémoire par hibernate


Alors le choix d'hibernate n'est pas adapté à ton problème.


Hibernate est bien adapté jusqu'à 2000 lignes (1 seconde)
A partir de 4000 ca commence à etre long (5 secondes chez moi)
6000 lignes c'est catastrophique, 10 secondes.
Multiplier les temps par 5-6 pour afficher la meme page depuis le serveur HP
(en comptant le transfert réseau adsl).

Mauvais outil changer d'outil.
Non. Tout le reste de l'appli fonctionne à merveille.

Seul le moteur de recherche souffre quand on lui demande d'extraire tout le
contenu de la base.
Plus encore mauvaise méthode changer de méthode.
Dans tous les cas, cela ne fera que repousser l'échéance.

Je trouve absurde de remonter une telle quantité de données.

J'espère que tu ne veux pas dire qu'à chaque fois que l'utilisateur
charge une page de 30 lignes hibernate charge 6000 lignes.


Non. Il recherche 6000 lignes, je lui en affiche 50 page page.
La pagination est gérée par struts-layout.
Je suppose qu'il la pose en session. C'est ce que je vérifie en ce moment,
mais je vois pas comment il ferait autrement.
Ceci dit une grande partie de la ligne est lazy loaded, donc chargée
uniquement lorsqu'elle est affichée.

Ni que tu
garde les 6000 lignes une fois pour toute dans un objet persistant
d'une requête à l'autre, genre ta session


Y a des chances :-)
Ceci dit je n'ai pas trop de problèmes de mémoire sur mon poste, meme avec
plusieurs utilisateurs.

Faire ça sur un serveur mutualisé ca devrait être passible de la cour
martiale. ;-)
Va savoir ce que font les autres :-)


Si tu avais la main sur la requête tu peux ne charger que 30 lignes à
la fois mais je ne suis pas sûr qu'Hibernate sache faire...
Oui. C'est la piste que je comptais étudier.

Une autre solution serait de définir un 2e mapping qui remonterait un peu
moins de colonnes.
(Mais je pense qu'elles sont toutes nécessaires.)

Pas ma boite. Mon client.
Perso j'aurais fait un bon gros cluster pour 10 fois moins cher et
10 fois plus performant.


Ce qui n'aurai pas résolu tes problèmes de performance qui n'ont rien
avoir avec le hard mais avec ton soft (celui que tu fais et celui que
tu intègre (hibernate)).
Alors comment expliques tu que la meme requete avec plusieurs utilisateurs

simultanés chez moi ne pose pas spécialement de probleme ?
(4-5 secondes).
Il y a quand meme probablement un problème sur ce serveur.

Sans vouloir paraître dur avec toi,
c'est gentil de t'inquiéter pour moi :-)

si tu ne sais pas que ton unix est
moins performant que ton poste de travail sur un traitement
monothreadé,
Moins performant je m'y attendais, mais que l'écart soit aussi énorme, je

m'y attendais moins.
je pense qu'il y a beaucoup de pièges pires dans lequel
tu tomberais avec un cluster
t'en fais pas, j'apprends vite.

Au fait si ce n'est pas la puissance CPU pure qui vaut le coup de
payer un unix cher, c'est peut-être autre chose... Je ne connais pas
ton client, mais un serveur unix ça a du hard redondant, ça sait
faire du scale-up (pour une base de données par exemple dans beaucoup
de cas (pas tous) les cluster et les trucs distribués c'est du pipo
marketing ça ne marche pas ou bcp moins bien)), c'est intégré
correctement dans les process et avec les produits faits pour faire
de la haute-dispo et du plan de secours (SAN, etc.).
Peut etre. Ceci dit quand une carte réseau tombe en panne, j'ai plus de

serveur pendant 1 journée complète.
alors la haute dispo et le plan de secours ca me fait bien rire.
Tout comme les autres appli avec lesquelle mon appli communique, qui
s'écroulent lamentablement alors qu'elles sont plus critiques que la mienne.

Mais peut-être que je me trompe. Que ton client n'a pas de besoin de
haute dispo, ni de plan de secours en cas d'incendie/dégâts des
eaux/chute d'avion.
Sur le meme serveur il y a une appli de cartes de voeux. Je pense que la

mienne a besoin d'une dispo un peu plus haute :-)
Peut-être juste que le commercial d'HP a les dents
blanches, auquel cas je comprends ta réaction.
Et pas uniquement celui de HP...


Merci pour les conseils.


Avatar
Derek Sagan
Derek Sagan wrote:

Ce qui me donne un tableau de 6000 lignes sur 30 colonnes.
Certes paginé, mais tout de meme chargé en mémoire par hibernate


Alors le choix d'hibernate n'est pas adapté à ton problème.



Hibernate est bien adapté jusqu'à 2000 lignes (1 seconde)
A partir de 4000 ca commence à etre long (5 secondes chez moi)
6000 lignes c'est catastrophique, 10 secondes.
Multiplier les temps par 5-6 pour afficher la meme page depuis le serveur HP
(en comptant le transfert réseau adsl).


Mauvais outil changer d'outil.


Non. Tout le reste de l'appli fonctionne à merveille.
Seul le moteur de recherche souffre quand on lui demande d'extraire tout le
contenu de la base.


Mauvais outil pour le module derecherche, change d'outil pour le module
de recherche. Je ne suis pas extremiste. Je ne veux pas forcément brûler
Hibernate sur la place publique.

Plus encore mauvaise méthode changer de méthode.


Dans tous les cas, cela ne fera que repousser l'échéance.
Je trouve absurde de remonter une telle quantité de données.


J'espère que tu ne veux pas dire qu'à chaque fois que l'utilisateur
charge une page de 30 lignes hibernate charge 6000 lignes.



Non. Il recherche 6000 lignes, je lui en affiche 50 page page.
La pagination est gérée par struts-layout.
Je suppose qu'il la pose en session. C'est ce que je vérifie en ce moment,
mais je vois pas comment il ferait autrement.
Ceci dit une grande partie de la ligne est lazy loaded, donc chargée
uniquement lorsqu'elle est affichée.


Oui c'est vrai c'est assez économe somme toute les trucs de fainéant
(j'utilise le mot pour "lazy" pas forcément pour toi, pas de
méprise...), ça ne prend que 80 Mo de RAM par utilisateur qui fait une
recherche.

Ni que tu
garde les 6000 lignes une fois pour toute dans un objet persistant
d'une requête à l'autre, genre ta session



Y a des chances :-)
Ceci dit je n'ai pas trop de problèmes de mémoire sur mon poste, meme avec
plusieurs utilisateurs.


D'où ma remarque suivante (un peu brutale certe) portant sur
l'importance de faire attention une fois qu'on n'est plus seul au monde.

Faire ça sur un serveur mutualisé ca devrait être passible de la cour
martiale. ;-)


Va savoir ce que font les autres :-)


Probablement comme toi. Qui a dit que j'étais contre les exécutions
collectives ?
C'est pour le bien des autres qu'il faut que tu arrêtes de prendre 80 Mo
par requête et c'est pour ton bien qu'on fusillera les autres, en
commençant par ceux qui prennent 90 Mo par requêtes sur des modules
appelés plus de fois simultanément que ton module de recherche.
;-)

Si tu avais la main sur la requête tu peux ne charger que 30 lignes à
la fois mais je ne suis pas sûr qu'Hibernate sache faire...


Oui. C'est la piste que je comptais étudier.
Une autre solution serait de définir un 2e mapping qui remonterait un peu
moins de colonnes.
(Mais je pense qu'elles sont toutes nécessaires.)


Pas ma boite. Mon client.
Perso j'aurais fait un bon gros cluster pour 10 fois moins cher et
10 fois plus performant.


Ce qui n'aurai pas résolu tes problèmes de performance qui n'ont rien
avoir avec le hard mais avec ton soft (celui que tu fais et celui que
tu intègre (hibernate)).


Alors comment expliques tu que la meme requete avec plusieurs utilisateurs
simultanés chez moi ne pose pas spécialement de probleme ?
(4-5 secondes).
Il y a quand meme probablement un problème sur ce serveur.


Oui, il tourne moins vite que ton poste de travail.
Et il y a peut-être d'autre (mauvais?) usages de la machine simultanément.
C'est pour ça que je fusillerais aussi les autres ;-)

Sans vouloir paraître dur avec toi,


c'est gentil de t'inquiéter pour moi :-)

si tu ne sais pas que ton unix est
moins performant que ton poste de travail sur un traitement
monothreadé,


Moins performant je m'y attendais, mais que l'écart soit aussi énorme, je
m'y attendais moins.

je pense qu'il y a beaucoup de pièges pires dans lequel
tu tomberais avec un cluster


t'en fais pas, j'apprends vite.

Au fait si ce n'est pas la puissance CPU pure qui vaut le coup de
payer un unix cher, c'est peut-être autre chose... Je ne connais pas
ton client, mais un serveur unix ça a du hard redondant, ça sait
faire du scale-up (pour une base de données par exemple dans beaucoup
de cas (pas tous) les cluster et les trucs distribués c'est du pipo
marketing ça ne marche pas ou bcp moins bien)), c'est intégré
correctement dans les process et avec les produits faits pour faire
de la haute-dispo et du plan de secours (SAN, etc.).


Peut etre. Ceci dit quand une carte réseau tombe en panne, j'ai plus de
serveur pendant 1 journée complète.
alors la haute dispo et le plan de secours ca me fait bien rire.
Tout comme les autres appli avec lesquelle mon appli communique, qui
s'écroulent lamentablement alors qu'elles sont plus critiques que la mienne.


C'est effectivement un peu dommage... surtout que le réseau c'est pas ce
qu'il y a de plus dur à durcir (la perte d'une carte réseau c'est plus
simple à couvrir que la perte d'un contrôleur fiber channel...)

Mais peut-être que je me trompe. Que ton client n'a pas de besoin de
haute dispo, ni de plan de secours en cas d'incendie/dégâts des
eaux/chute d'avion.


Sur le meme serveur il y a une appli de cartes de voeux. Je pense que la
mienne a besoin d'une dispo un peu plus haute :-)


:-)

Peut-être juste que le commercial d'HP a les dents
blanches, auquel cas je comprends ta réaction.


Et pas uniquement celui de HP...

Merci pour les conseils.


De rien, au bout du compte à part te dire (très fort) que ton serveur va
moins vite que ton PC je n'ai pas dis grand chose...

a+



Avatar
jlp
Derek Sagan wrote:

Cela dit très respectueusement, c'est complètement irresponsable de
ramener autant d'objets, pour peu qu'ils soient bien gras c'est
sûrement là que sont tes 80 Mo.


Je suis bien d'accord. Je sais d'ou viennent mes 80Mo.
Mais les utilisateurs veulent pouvoir remonter 1 mois de données d'un seul
coup.
Ce qui me donne un tableau de 6000 lignes sur 30 colonnes.
Certes paginé, mais tout de meme chargé en mémoire par hibernate


Parce qu'un CPU de ton serveur est environe 2 fois moins rapide à
parcourir un iterateur que celui de ton poste de travail.


Très probable.

Sur le papier dont tu parles ton pa-risc ou ton itanium (je sais pas
ce que c'est ton hpux risc)


moi non plus

torche un PC parce qu'il est testé avec
une application consommatrice d'I/O (ce en quoi le PC est mauvais et
un serveur unix bon) et surtout multithreadé donc capable d'utiliser
plusieurs CPU en même temps.
un CPU intel torche n'importe quel cpu 64 bits riscs avec
turbo-compresseur à induction nucléaire.


Bien vu.

A vue de nez je dirais que tu as un pentium entre 1.5GHz et 2GHz comme


PIV 3GHz.

poste de travail, ça doit être à peu près le ratio pour faire fois 2
avec ton 64 bits 750 MHz
Désolé d'égratigner un mythe, ce n'est pas pour sa puissance CPU pure
(un CPU à la fois) que ta boîte paye si cher HP.


Pas ma boite. Mon client.
Perso j'aurais fait un bon gros cluster pour 10 fois moins cher et 10 fois
plus performant.


Ou bien
quelqu'un d'autre utilise le serveur en même temps que toi et le
sature.


Va savoir, il y a plus de 20 autres applis sur ce serveur.

Mais de loin le plus probable c'est ce que je dis ci-dessus.


Tu m'as convaincu


Merci.


Ne connaissant pas votre niveau de connaissance sur Hibernate,

ci-dessous quelques points notés sur une première expérience Hibernate.
Avec Hibernate, ( et autres ORM certainement), il faut faire trés
attention à la gestion des sessions : session longue ou plusieurs
session courtes ; Dans le second cas on n'optimise pas l'utilisation du
cache hibernate au maximun.
Jouer aussi avec les paramétres de chargement ( leazy ) pour tuner.
Le parametre sql_show à true permet de voir d'une part le code généré et
les acces bases.
Pour ma part je trouve que Hibernate est un tres bon produit.


Avatar
Derek Sagan

Derek Sagan wrote:

Cela dit très respectueusement, c'est complètement irresponsable de
ramener autant d'objets, pour peu qu'ils soient bien gras c'est
sûrement là que sont tes 80 Mo.



Je suis bien d'accord. Je sais d'ou viennent mes 80Mo.
Mais les utilisateurs veulent pouvoir remonter 1 mois de données d'un
seul coup.
Ce qui me donne un tableau de 6000 lignes sur 30 colonnes.
Certes paginé, mais tout de meme chargé en mémoire par hibernate


Parce qu'un CPU de ton serveur est environe 2 fois moins rapide à
parcourir un iterateur que celui de ton poste de travail.



Très probable.

Sur le papier dont tu parles ton pa-risc ou ton itanium (je sais pas
ce que c'est ton hpux risc)



moi non plus

torche un PC parce qu'il est testé avec
une application consommatrice d'I/O (ce en quoi le PC est mauvais et
un serveur unix bon) et surtout multithreadé donc capable d'utiliser
plusieurs CPU en même temps.
un CPU intel torche n'importe quel cpu 64 bits riscs avec
turbo-compresseur à induction nucléaire.



Bien vu.

A vue de nez je dirais que tu as un pentium entre 1.5GHz et 2GHz comme



PIV 3GHz.

poste de travail, ça doit être à peu près le ratio pour faire fois 2
avec ton 64 bits 750 MHz
Désolé d'égratigner un mythe, ce n'est pas pour sa puissance CPU pure
(un CPU à la fois) que ta boîte paye si cher HP.



Pas ma boite. Mon client.
Perso j'aurais fait un bon gros cluster pour 10 fois moins cher et 10
fois plus performant.


Ou bien
quelqu'un d'autre utilise le serveur en même temps que toi et le
sature.



Va savoir, il y a plus de 20 autres applis sur ce serveur.

Mais de loin le plus probable c'est ce que je dis ci-dessus.



Tu m'as convaincu


Merci.

Ne connaissant pas votre niveau de connaissance sur Hibernate,

ci-dessous quelques points notés sur une première expérience Hibernate.
Avec Hibernate, ( et autres ORM certainement), il faut faire trés
attention à la gestion des sessions : session longue ou plusieurs
session courtes ; Dans le second cas on n'optimise pas l'utilisation du
cache hibernate au maximun.
Jouer aussi avec les paramétres de chargement ( leazy ) pour tuner.
Le parametre sql_show à true permet de voir d'une part le code généré et
les acces bases.
Pour ma part je trouve que Hibernate est un tres bon produit.


Oui Canard WC aussi, et pourtant je ne me lave pas les dents avec.
;-)



Avatar
Lionel
jlp wrote:
. Avec Hibernate, ( et autres ORM certainement), il faut
faire trés attention à la gestion des sessions : session longue ou
plusieurs session courtes ; Dans le second cas on n'optimise pas
l'utilisation du cache hibernate au maximun.


A mon avis, en web il n'y a pas 36 façons de gérer la session hibernate:
j'ouvre une session pour chaque requete Http via un servlet filter, fermée
en fin de requete.
Concernant le cache, j'utilise le cache de jboss comme cache de second
niveau, cela fonctionne très bien pour les tables de référence auxquelles on
accède souvent, qui sont rarement modifiées, et qui contiennent très peu de
lignes.

Jouer aussi avec les paramétres de chargement ( leazy ) pour tuner.


un conseil: regarde aussi du coté des outer-joins.

Le parametre sql_show à true permet de voir d'une part le code généré
et les acces bases.
Qui est très propre dans mon cas.

Personnellement, je regarde la gueule de mes requetes et leur plan
d'execution directement dans oracle.

Pour ma part je trouve que Hibernate est un tres bon produit.
Idem.


Avatar
Lionel
Derek Sagan wrote:
Mauvais outil pour le module derecherche, change d'outil pour le
module de recherche.


Je pense pouvoir m'en sortir avec hibernate.
Faut juste trouver la bonne solution, adaptée au besoin.


Oui c'est vrai c'est assez économe somme toute les trucs de fainéant
(j'utilise le mot pour "lazy" pas forcément pour toi, pas de
méprise...), ça ne prend que 80 Mo de RAM par utilisateur qui fait une
recherche.
Alors imagine sans les trucs de fénéant lol :-)

Comme précisé dans un autre message, j'essaierai de définir un 2e mapping un
peu plus léger.
Reste à trouver les colonnes qui ne sont pas indispensables et qui peuvent
etre supprimées du mapping.
L'autre solution serait de ne remonter que les valeurs des colonnes choisies
par l'utilisateur (il a une liste de colonnes dans lesquelles il peut
piocher ce qu'il veut), ce qui sera probablement le plus efficace, mais qui
me demandera le plus de travail :-)

C'est pour le bien des autres qu'il faut que tu arrêtes de prendre 80
Mo par requête
La mémoire que je consomme ne sera à priori pas consommée par les autres

(enfin ca dépend des paramètres passés à la JVM mais je suppose qu'un maxi
est donné à tout le monde)
Par contre si je gère mal la mémoire qui m'est allouée, je risque de garbage
collecter un max, et donc de faire ramer tout le monde.
Le CPU est LA resource à économiser. Et ca passe par une bonne gestion de la
mémoire.

De rien, au bout du compte à part te dire (très fort) que ton serveur
va moins vite que ton PC je n'ai pas dis grand chose...


Certes, mais plus j'écris, plus je trouve de solutions potentielles.
Bizarrement dans ce NG, j'ai toujours tendance à me répondre à moi meme,
mais pour une fois il y en a d'autres qui participent. ;-)

Avatar
jlp

[SNIP]
L'autre solution serait de ne remonter que les valeurs des colonnes choisies
par l'utilisateur (il a une liste de colonnes dans lesquelles il peut
piocher ce qu'il veut), ce qui sera probablement le plus efficace, mais qui
me demandera le plus de travail :-)
[SNIP]


En récupérant la connexion de ta session Hibernate (
session.getConnexion) et en faisant des requetes en sql natif Oracle,
plus facilement customisable ( avec l'objet PreparedStatement) . Mais là
évidemment on détourne un peu le concept O/R .

Avatar
Lionel
jlp wrote:

[SNIP]
L'autre solution serait de ne remonter que les valeurs des colonnes
choisies par l'utilisateur (il a une liste de colonnes dans
lesquelles il peut piocher ce qu'il veut), ce qui sera probablement
le plus efficace, mais qui me demandera le plus de travail :-)
[SNIP]


En récupérant la connexion de ta session Hibernate (
session.getConnexion) et en faisant des requetes en sql natif Oracle,
plus facilement customisable ( avec l'objet PreparedStatement) . Mais
là évidemment on détourne un peu le concept O/R .


Le problème c'est que j'utilise une requete Criteria et pas une requete HQL.
En HQL, customiser le résultat remonté pour ne remonter que ce que l'on veut
est facile.
Avec un criteria je vois pas trop comment faire.


Avatar
Lionel
Après un nouveau test, voici les résultats.

J'ai créé un nouveau mapping pour le meme objet, en virant toutes les
colonnes/relations qui ne sont pas utilisées par le moteur de recherche.
La requete qui prenait 5-6 secondes et occupait 80Mo ne prend maintenant
plus que 3.5 secondes et 50Mo (pour un tableau de 6000 lignes croisant les
données de 6 ou 7 tables).
Les requetes qui prenaient auparavant 3s sont tombées à 1.5s (pour 4000
lignes).
Faire mieux me semble difficile.
1 2