OVH Cloud OVH Cloud

mémoire et strings

22 réponses
Avatar
Pif
Bonjour, quelqu'un connait il un endroit précis sur lequel on peut
trouver de l'info sur la gestion des strings en java, le persitence et
destruction via le GC...


j'ai besoin de manipuler beaucoup de chaines de caractères dont une
grande partie sont très temporaires... je veux pouvoir faciliter le
travail pour gagner en perfs !

merci !

10 réponses

1 2 3
Avatar
Hervé AGNOUX
Pif wrote:

Bonjour, quelqu'un connait il un endroit précis sur lequel on peut
trouver de l'info sur la gestion des strings en java, le persitence et
destruction via le GC...



Il y a pas mal d'infos partout, mais je n'ai pas conservé les liens :-)

Si tu y tiens et si personne d'autre ne s'en occupe, je chercherai.



j'ai besoin de manipuler beaucoup de chaines de caractères dont une
grande partie sont très temporaires... je veux pouvoir faciliter le
travail pour gagner en perfs !



La principale chose pour faciliter les perfs est de ne rien faire. (à part
éviter d'accumuler tes chaînes temporaires dans un tableau statique, par
exemple). Le ramasse miettes se débrouillera tout seul et libèrera les
Strings qui ne sont plus référencés dans ton programme, sans que tu aies
besoin de le faire.

Sinon il y a quantité de trucs à base de tableaux de caractères, de
StringBuffer, de String.intern() etc, mais aucune recette miracle. La
meilleure voie est de concevoir ton appli de façon à ce que ce qui est
temporaire soit temporaire, et ce qui est permanent soit permanent, et
ainsi de suite.

Peut être peux-tu nous en dire plus ?

Bon courage.


--
Hervé AGNOUX
http://www.diaam-informatique.com

Avatar
Pif
Bon, ci dessous le code de la classe, le principe est simple, par
sous-ensembles de taille gap, je parcours une liste de citation d'une
base bibliographique (PubMed).

Pour chaque abstract je récupère un ArrayList qui est une liste de ses
termes.

A chaque terme rencontré, je l'insère dans mon Hashtable (qui me sert de
Hashset, mais bizarrement un Hashtable prend 4 fois moins de mémoire
qu'un HashSet ???? ).
Ensuite, quand mon hashtable atteint une taille limite (statBufferSize)
j'invoque la méthode flush qui prend tous les termes contenus, fait un
select pour voir s'il est déjà inséré, et l'ajoute sinon dans la table.
Les ordres de grandeurs sont : 7 000 000 d'articles et abstract d'une
centaine de mot en moyenne grossomodo, et je flush par 400 000 à 600
000, ca serait bien mieux si je pouvais faire mieux .

Alors, mes questions sont :
- quand je prend un hashset ou hashtable et que je le rempli d'un
millions de termes avec une boucle for par exemple, j'ai une taille
mémoire qui représente près de 65 MO, pour 2 millions de termes près de
100 Mo. Dans ce programme, quand j'attent une hashtable de 600 000
termes j'ai déjà plus d' 1Go de RAM qui est prise... alors qu'est ce qui
me coute autant ?

- c'est encore raisonnable, mais la requete flush est ultra-lente, (nb
j'ai bien un index sur le champ de mon select, l'autre champ est la cle
primaire qui possède un auto-increment). Mais mon étonnnement : quand
fait ces requetes, ma mémoire de mon programme java continue à grossir,
et la mémoire de mon mysqld grossit fortement aussi, le tout explose au
bout de peu de temps, mysql n'arrivant plus à suivre j'ai
l'impression... J'ai pourtant essayé pas mal d'option, l'autocommit et
le commit manuel, etc...

Alors merci d'avance si vous avez une solutions à mes problèmes...








private Hashtable termset;

public void indexTerms(int start, int end) {
termset = new Hashtable((int) (statBufferSize * 1.25));
for (int i = start; i < end; i += gap) {
int endtmp=i+gap;
if (endtmp > end) endtmp = end;
processCitationForTermIndex(i, i + gap);
if (termset.size() > statBufferSize)
flushTerms();
}
flushTerms();
}
private void processCitationForTermIndex(int start, int end) {
System.gc();
cpt = 0;
try {
PreparedStatement st = connection
.prepareStatement("Select * from " + sourceTable
+ " where pmid >= ? and pmid < ? ;");
st.setInt(1, start);
st.setInt(2, end);
ResultSet rs = st.executeQuery();
if (rs.first()) {
boolean looptest = true;
while (looptest) {
if (rs.isLast())
looptest = false;
cpt++;
if (cpt % gap == 0) {
System.out.println("Citation : " + cpt
+ " PMID = " + rs.getInt("pmid")+ " Hashtab size : "+
termset.size());
}
processTerms(rs);
rs.next();
}
}
rs.close();rs=null;
st.close();st=null;
} catch (Exception e) {
System.out.println("Erreur dans l'enrichissement de la base medline");
System.out.println(e);
e.printStackTrace();
}
}


private void processTerms(ResultSet rs) {
try {
String s = rs.getString("title");
if (s != null && s.length() > 2) {
ArrayList al=TextTools.decodeArrayListFromString(s, "<T>","</T>");
if (al!=null){
Object o;
for (int i=0;i<al.size();i++){
o=al.get(i);
if(o!=null) termset.put(o,o);
}
al.clear();
al=null;
}
}
s=null;
s = rs.getString("abstract");
if (s != null && s.length() > 2) {
ArrayList al=TextTools.decodeArrayListFromString(s, "<T>","</T>");
if (al!=null){
Object o;
for (int i=0;i<al.size();i++){
o=al.get(i);
if(o!=null) termset.put(o,o);
}
al.clear();
al=null;
}
}
s=null;
s = rs.getString("vernaculartitle");
if (s != null && s.length() > 2) {
ArrayList al=TextTools.decodeArrayListFromString(s, "<T>","</T>");
if (al!=null){
Object o;
for (int i=0;i<al.size();i++){
o=al.get(i);
if(o!=null) termset.put(o,o);
}
al.clear();
al=null;
}
}
s=null;
} catch (Exception e) {
System.out
.println("Erreur dans l'enrichissement de la base medline");
System.out.println(e);
e.printStackTrace();
}
}

private void flushTerms() {
int cpt2 = 0;
for (Enumeration e = termset.keys(); e.hasMoreElements();) {

cpt2++;
if (cpt2 % 10000 == 0){
System.out.println("Insertion terme :: " + cpt2);
}
insertTerm((String) e.nextElement());
}
termset.clear();
termset = null;
System.gc();
termset= new Hashtable((int) (statBufferSize * 1.25));
}
private void insertTerm(String s){
try {
if (s != null) {
PreparedStatement st = connection
.prepareStatement("select * from " + targetTable
+ " where term = ? ;");
st.setString(1, s);
ResultSet rs = st.executeQuery();
if (!rs.first()) {
rs.close();
st.close();
st = connection.prepareStatement("insert HIGH_PRIORITY into "
+ targetTable + " ( term ) values (?);");
st.setString(1, s);

st.executeUpdate();
st.close();
}
}
} catch (Exception e) {
System.out.println("Erreur de recherche / insertion d'un terme");
System.out.println(e);
e.printStackTrace();
}
}




Avatar
Kupee
Pif wrote:
Bon, ci dessous le code de la classe, le principe est simple, par
sous-ensembles de taille gap, je parcours une liste de citation d'une
base bibliographique (PubMed).

Pour chaque abstract je récupère un ArrayList qui est une liste de ses
termes.

A chaque terme rencontré, je l'insère dans mon Hashtable (qui me sert de
Hashset, mais bizarrement un Hashtable prend 4 fois moins de mémoire
qu'un HashSet ???? ).
Ensuite, quand mon hashtable atteint une taille limite (statBufferSize)
j'invoque la méthode flush qui prend tous les termes contenus, fait un
select pour voir s'il est déjà inséré, et l'ajoute sinon dans la table.
Les ordres de grandeurs sont : 7 000 000 d'articles et abstract d'une
centaine de mot en moyenne grossomodo, et je flush par 400 000 à 600
000, ca serait bien mieux si je pouvais faire mieux .

Alors, mes questions sont :
- quand je prend un hashset ou hashtable et que je le rempli d'un
millions de termes avec une boucle for par exemple, j'ai une taille
mémoire qui représente près de 65 MO, pour 2 millions de termes près de
100 Mo. Dans ce programme, quand j'attent une hashtable de 600 000
termes j'ai déjà plus d' 1Go de RAM qui est prise... alors qu'est ce qui
me coute autant ?

- c'est encore raisonnable, mais la requete flush est ultra-lente, (nb
j'ai bien un index sur le champ de mon select, l'autre champ est la cle
primaire qui possède un auto-increment). Mais mon étonnnement : quand
fait ces requetes, ma mémoire de mon programme java continue à grossir,
et la mémoire de mon mysqld grossit fortement aussi, le tout explose au
bout de peu de temps, mysql n'arrivant plus à suivre j'ai
l'impression... J'ai pourtant essayé pas mal d'option, l'autocommit et
le commit manuel, etc...

Alors merci d'avance si vous avez une solutions à mes problèmes...








private Hashtable termset;

public void indexTerms(int start, int end) {
termset = new Hashtable((int) (statBufferSize * 1.25));
for (int i = start; i < end; i += gap) {
int endtmp=i+gap;
if (endtmp > end) endtmp = end;
processCitationForTermIndex(i, i + gap);
if (termset.size() > statBufferSize)
flushTerms();
}
flushTerms();
}
private void processCitationForTermIndex(int start, int end) {
System.gc();
cpt = 0;
try {
PreparedStatement st = connection
.prepareStatement("Select * from " + sourceTable
+ " where pmid >= ? and pmid < ? ;");
st.setInt(1, start);
st.setInt(2, end);
ResultSet rs = st.executeQuery();
if (rs.first()) {
boolean looptest = true;
while (looptest) {
if (rs.isLast())
looptest = false;
cpt++;
if (cpt % gap == 0) {
System.out.println("Citation : " + cpt
+ " PMID = " + rs.getInt("pmid")+ "
Hashtab size : "+ termset.size());
}
processTerms(rs);
rs.next();
}
}
rs.close();rs=null;
st.close();st=null;
} catch (Exception e) {
System.out.println("Erreur dans l'enrichissement de la base
medline");
System.out.println(e);
e.printStackTrace();
}
}


private void processTerms(ResultSet rs) {
try {
String s = rs.getString("title");
if (s != null && s.length() > 2) {
ArrayList al=TextTools.decodeArrayListFromString(s,
"<T>","</T>");
if (al!=null){
Object o;
for (int i=0;i<al.size();i++){
o=al.get(i);
if(o!=null) termset.put(o,o);
}
al.clear();
al=null;
}
}
s=null;
s = rs.getString("abstract");
if (s != null && s.length() > 2) {
ArrayList al=TextTools.decodeArrayListFromString(s,
"<T>","</T>");
if (al!=null){
Object o;
for (int i=0;i<al.size();i++){
o=al.get(i);
if(o!=null) termset.put(o,o);
}
al.clear();
al=null;
}
}
s=null;
s = rs.getString("vernaculartitle");
if (s != null && s.length() > 2) {
ArrayList al=TextTools.decodeArrayListFromString(s,
"<T>","</T>");
if (al!=null){
Object o;
for (int i=0;i<al.size();i++){
o=al.get(i);
if(o!=null) termset.put(o,o);
}
al.clear();
al=null;
}
}
s=null;
} catch (Exception e) {
System.out
.println("Erreur dans l'enrichissement de la base
medline");
System.out.println(e);
e.printStackTrace();
}
}

private void flushTerms() {
int cpt2 = 0;
for (Enumeration e = termset.keys(); e.hasMoreElements();) {

cpt2++;
if (cpt2 % 10000 == 0){
System.out.println("Insertion terme :: " + cpt2);
}
insertTerm((String) e.nextElement());
}
termset.clear();
termset = null;
System.gc();
termset= new Hashtable((int) (statBufferSize * 1.25));
}
private void insertTerm(String s){
try {
if (s != null) {
PreparedStatement st = connection
.prepareStatement("select * from " + targetTable
+ " where term = ? ;");
st.setString(1, s);
ResultSet rs = st.executeQuery();
if (!rs.first()) {
rs.close();
st.close();
st = connection.prepareStatement("insert HIGH_PRIORITY
into "
+ targetTable + " ( term ) values (?);");
st.setString(1, s);

st.executeUpdate();
st.close();
}
}
} catch (Exception e) {
System.out.println("Erreur de recherche / insertion d'un terme");
System.out.println(e);
e.printStackTrace();
}
}


Euh je me pose une question : a quoi te sert la termset ? Quelle est
l'utilité de stocker tout ca en mémoire ? Au final ils vont tous finir
par etre insérés dans la base de données non ?
A ce moment pourquoi ne pas faire directement cette insertion dès la
récupération des termes ?

Une autre idée, mais je sais pas ce qu'elle vaut : mettre un index
unique sur le champ term et faire directement l'insert sans vérifier que
le terme existe déjà dans ta table.
Essaye aussi avec et sans indexes sur la table, car l'index peut
accélérer le select mais va certainement ralentir l'insert ...

Avatar
Pif

Euh je me pose une question : a quoi te sert la termset ? Quelle est
l'utilité de stocker tout ca en mémoire ? Au final ils vont tous finir
par etre insérés dans la base de données non ?
A ce moment pourquoi ne pas faire directement cette insertion dès la
récupération des termes ?


en fait il s'agit de corpus de termes du langage naturel, certains mots
apparaissent très fréquemment, par exemples les déterminents, pronoms,
"a", "the", "do", "have", "be" etc...


Donc le termset me sert de cache, je fais ainsi près de 100 insertions
dans mon hashset pour une effectuée dans la table, ce qui est bien plus
long...

Une autre idée, mais je sais pas ce qu'elle vaut : mettre un index
unique sur le champ term et faire directement l'insert sans vérifier que
le terme existe déjà dans ta table.


tu peux faire un autoincrement sur un champ qui n'est pas clef ?
que se passe-t-il s'il si un tuple n'est pas inséré à cause d'un autre
existent au niveau de l'increment ?

dans tous les cas ca ne fait que reporter le probblème, j'ai d'autres
calculs plus tard similaires mais qui auront des champs en plus... et
qui seront beaucoup plus lourds et nombreux...

Essaye aussi avec et sans indexes sur la table, car l'index peut
accélérer le select mais va certainement ralentir l'insert ...


l'index est indispensable...

Avatar
Kupee
Pif wrote:
en fait il s'agit de corpus de termes du langage naturel, certains mots
apparaissent très fréquemment, par exemples les déterminents, pronoms,
"a", "the", "do", "have", "be" etc...


Tu ne pourrais faire un passage pour répertorier les mots les plus
courants, et ne vérifier que dans cette liste et ensuite compter sur la
clef unique de ta table pour refuser un mot déjà existant ?

tu peux faire un autoincrement sur un champ qui n'est pas clef ?
que se passe-t-il s'il si un tuple n'est pas inséré à cause d'un autre
existent au niveau de l'increment ?


Je pensais a une clef sur le champ term en fait

dans tous les cas ca ne fait que reporter le probblème, j'ai d'autres
calculs plus tard similaires mais qui auront des champs en plus... et
qui seront beaucoup plus lourds et nombreux...

Essaye aussi avec et sans indexes sur la table, car l'index peut
accélérer le select mais va certainement ralentir l'insert ...


l'index est indispensable...


Ok, ca dépend effectivement de ton projet ca.
Bon mon idée sur l'index n'était peut etre pas adaptée a ton besoin


Avatar
Kupee
Pif wrote:

Euh je me pose une question : a quoi te sert la termset ? Quelle est
l'utilité de stocker tout ca en mémoire ? Au final ils vont tous finir
par etre insérés dans la base de données non ?
A ce moment pourquoi ne pas faire directement cette insertion dès la
récupération des termes ?



en fait il s'agit de corpus de termes du langage naturel, certains mots
apparaissent très fréquemment, par exemples les déterminents, pronoms,
"a", "the", "do", "have", "be" etc...


Donc le termset me sert de cache, je fais ainsi près de 100 insertions
dans mon hashset pour une effectuée dans la table, ce qui est bien plus
long...


Tiens tu peux peut etre regarder de ce coté :
http://trove4j.sourceforge.net/
C'est une librairie contenant des Maps, des Sets et autre occupant moins
de mémoire et plus rapides que ceux de l'api java en général.
Peut etre que tu trouveras ton bonheur.


Avatar
Hervé AGNOUX
Pif wrote:

Bon, ci dessous le code de la classe, le principe est simple, par
sous-ensembles de taille gap, je parcours une liste de citation d'une
base bibliographique (PubMed).



Il me semble qu'il faudrait séparer les problèmes dus à la gestion de la
base de données, et ceux dus à la gestion des maps. Idéalement, faire deux
classes séparées, que l'on puisse évaluer chacune dans son coin.

Pour la base de données, je ne sais pas. Pour les maps, le point sensible
est le calcul du hashcode et le fait que ces objets ont tendance à se
réorganiser selon l'ordre des hashcodes (forcément) ; il ne faut donc pas
le provoquer trop souvent. Ce que je ne comprends pas dans ces maps, c'est
quel est le couple clef-valeur exact ?

Si je comprends bien, tes maps ne te servent qu'à determiner si tu dois
enregistrer une donnée dans ta base ? Si un terme n'est pas encore présent
dans la base, alors tu l'enregistre, sinon tu ne l'enregistre pas, c'est
ça ?


--
Hervé AGNOUX
http://www.diaam-informatique.com

Avatar
Pif
ce n'est pas la qu'est le problème fait moi confiance.

La question, c'est comment lancer 1 000 000 de requetes en faisant en
sorte que la mémoire n'en prenne pas trop et que simplement les requetes
se détruisent au fur et a mesures sans occuper de mémoire ? Ce que
contient le termset n'a pas de rapport...
Avatar
Pif
je connais, mais les résultats ne sont réellement convainquant que pour
les arraylist.

En plus la performance de ces outils est bonne, mais n'a pas de rapport
avec le problème de la destruction d'objet... le problème n'est pas
celui de mon hashset qui est quasi optimal mais du nombre d'objets
independamment de la classe qu'ils instancient...
Avatar
Pif

Pif wrote:


Bon, ci dessous le code de la classe, le principe est simple, par
sous-ensembles de taille gap, je parcours une liste de citation d'une
base bibliographique (PubMed).




Il me semble qu'il faudrait séparer les problèmes dus à la gestion de la
base de données, et ceux dus à la gestion des maps. Idéalement, faire deux
classes séparées, que l'on puisse évaluer chacune dans son coin.

Pour la base de données, je ne sais pas. Pour les maps, le point sensible
est le calcul du hashcode et le fait que ces objets ont tendance à se
réorganiser selon l'ordre des hashcodes (forcément) ; il ne faut donc pas
le provoquer trop souvent. Ce que je ne comprends pas dans ces maps, c'est
quel est le couple clef-valeur exact ?


j'avais un hashset, mais en faisant un test, j'ai constaté qu'un
hashtable ou on rentre une clef et n'importe quoi ensuite, avec une
simple detection du résultat = null était plus économique, alors j'ai
remplacé ... mais ca change pas grand chose... c'est pas la le problème...


Si je comprends bien, tes maps ne te servent qu'à determiner si tu dois
enregistrer une donnée dans ta base ? Si un terme n'est pas encore présent
dans la base, alors tu l'enregistre, sinon tu ne l'enregistre pas, c'est
ça ?



heu... oui et non ...
c'est ca en effet, mais cette map ne contient pas tous les termes de la
base... c'est une sorte de buffer... j'ai plusieurs millions de termes
différents...


1 2 3