2014-07-24 10 views
9

Ich arbeite an einem J2EE-Projekt, bei dem Postleitzahlen, Städte und Länder zusammen gespeichert werden. Wir haben eine Java-Klasse entwickelt, die die Integration jeder Länderdatei (mit jeder Postleitzahl und jeder Stadt) übernimmt. Das Problem ist, dass für einige Länder (Großbritannien, Niederlande ...) die Datei ziemlich konsequent ist (400.000 bis 800.000 Zeilen).Java - Läuft eine Schleife jedes Mal länger?

Ich habe eine while() Schleife, die die nächste Zeile liest, erhält die Informationen und speichert sie in meiner Datenbank. Das Problem ist, dass für die 1000 oder 10.000 ersten Zeilen der Prozess schnell ist, sehr schnell, dann scheint es jedes Mal langsamer zu werden, wenn es durch die Schleife geht, dann passiert ein HeapSpaceOverflowException nach 150.000 Zeilen.

Ich dachte zuerst, dass ein Objekt nicht Müll gesammelt und verlangsamt meinen Algorithmus, aber ich kann nicht herausfinden, welche. Außerdem, wenn ich diesen Algorithmus auf meinem PC, JConsole sagt mir, dass Heap-Speicher wird regelmäßig gereinigt (scheint Müll gesammelt werden), aber der Prozess ist immer langsamer und langsamer ...

Unten ist der Code der Methode :

Variable this.pc wird durch @Inject Annotation injiziert.

Kann mir jemand helfen herauszufinden, warum dieser Code immer langsamer wird?

Vielen Dank.

Edit: Zur Vervollständigung willen, habe ich den Code der get...() Methode hinzugefügt:

public Codepostalville getByCodePostalAndVilleAndINSEE(String codePostal, String ville, 
                 String pays, String codeINSEE) throws DatabaseException 
{ 
    Codepostal cp = null; Ville v = null; PPays p = null; Codepostalville cpv = null; 

    try 
    { 
     // Tout d'abord, il faut retrouver l'objet CodePostal 
     cp = (Codepostal) this.em 
         .createNamedQuery("Codepostal.findByCodePostal") 
         .setParameter("codePostal", codePostal) 
         .getSingleResult(); 
    } 
    catch (NoResultException nre1) 
    { 
     // Si on ne l'a pas trouvé, on le crée 
     if (cp == null) 
     { 
      cp = new Codepostal(); 
      cp.setCodePostal(codePostal); 
      cpc.getFacade().create(cp); 
     } 
    } 

    // On retrouve la ville... 
    try 
    { 
     // Le nom de la ville passé par l'utilisateur doit être purgé (enlever 
     // les éventuels tirets, caractères spéciaux...) 
     // On crée donc un nouvel objet Ville, auquel on affecte le nom à purger 
     // On effectue la purge, et on récupère le nom purgé 
     Ville purge = new Ville(); 
     purge.setNomVille(ville); 
     purge.purgerNomVille(); 
     ville = purge.getNomVille(); 

     v = (Ville) this.em 
         .createNamedQuery("Ville.findByNomVille") 
         .setParameter("nomVille", ville) 
         .getSingleResult(); 
    } 
    catch (NoResultException nre2) 
    { 
     // ... ou on la crée si elle n'existe pas 
     if (v == null) 
     { 
      v = new Ville(); 
      v.setNomVille(ville); 
      vc.getFacade().create(v); 
     } 
    } 

    // On retrouve le pays 
    try 
    { 
     p = (PPays) this.em 
         .createNamedQuery("PPays.findByNomPays") 
         .setParameter("nomPays", pays) 
         .getSingleResult(); 
    } 
    catch (NoResultException nre2) 
    { 
     // ... ou on la crée si elle n'existe pas 
     if (p == null) 
     { 
      p = new PPays(); 
      p.setNomPays(pays); 
      pc.getFacade().create(p); 
     } 
    } 

    // Et on retrouve l'objet CodePostalVille 
    try 
    { 
     cpv = (Codepostalville) this.em 
       .createNamedQuery("Codepostalville.findByIdVilleAndIdCodePostalAndIdPays") 
       .setParameter("idVille", v) 
       .setParameter("idCodePostal", cp) 
       .setParameter("idPays", p) 
       .getSingleResult(); 

     // Si on a trouvé l'objet CodePostalVille, on met à jour son code INSEE 
     cpv.setCodeINSEE(codeINSEE); 
     this.getFacade().edit(cpv); 
    } 
    catch (NoResultException nre3) 
    {   
     if (cpv == null) 
     { 
      cpv = new Codepostalville(); 
      cpv.setIdCodePostal(cp); 
      cpv.setIdVille(v); 
      cpv.setCodeINSEE(codeINSEE); 
      cpv.setIdPays(p); 
      this.getFacade().create(cpv); 
     } 
    } 

    return cpv; 
} 

Nochmals vielen Dank.

Edit 2: Also, ich habe ein paar mehr Informationen. Die Methode getCodePostal...() benötigt ungefähr 15 ms, um am Anfang der Schleife ausgeführt zu werden, und nach 10.000 Zeilen benötigt sie mehr als 100 ms, um ausgeführt zu werden (fast 10 mal mehr!). In dieser neuen Version habe ich den Commit/Rollback-Code deaktiviert, so dass jede Abfrage sofort ausgeführt wird.

Ich kann nicht wirklich finden, warum es mehr und mehr Zeit braucht.

Ich habe einige Informationen über JPA-Cache zu suchen versucht: Meine aktuelle Konfiguration ist dies (in persistence.xml):

<property name="eclipselink.jdbc.bind-parameters" value="true"/> 
    <property name="eclipselink.jdbc.cache-statements" value="true"/> 
    <property name="eclipselink.cache.size.default" value="10000"/> 
    <property name="eclipselink.query-results-cache" value="true"/> 

Ich weiß nicht, ob es die effizienteste Konfiguration ist, und ich würde mich über Hilfe und einige Erklärungen zum JPA-Cache freuen.

Danke.

+0

Was machen getByCodePays und getByCodePostalAndVilleAndINSEE? Und hast du schon einen Profiler benutzt? –

+0

Haben Sie überprüft, welcher Teil der Engpass Ihrer Implementierung ist? Führen Sie Prüfungen durch, die von der Anzahl der Datenbankelemente beeinflusst werden können? – Eypros

+0

Gibt es eine Möglichkeit, große Dateien in kleinere Dateien zu zerlegen. Und dann durch Executor, lesen Sie jedes Segment und Prozess? –

Antwort

12

Sie können sich über JPA-Konzepte informieren. Kurz gesagt, ein EntityManager ist einem Persistenzkontext zugeordnet, der einen Verweis auf alle durch ihn manipulierten persistenten Objekte behält, so dass er alle Änderungen, die an diesen Objekten vorgenommen werden, zurück in die Datenbank schreiben kann.

Da Sie niemals den Persistenzkontext schließen, ist dies die wahrscheinliche Ursache für Ihr Speicherleck. Darüber hinaus muss ein Persistenzanbieter Änderungen an persistenten Objekten in die Datenbank schreiben, bevor eine Abfrage ausgegeben wird, wenn diese Änderungen das Ergebnis der Abfrage ändern können. Um diese Änderungen zu erkennen, ist eine Iteration über alle Objekte erforderlich, die dem aktuellen persistenten Kontext zugeordnet sind. In Ihrem Code sind dies fast eine Million Objekte für jede von Ihnen gestellte Abfrage.

Daher sollten Sie den Persistenzkontext zumindest in regelmäßigen Abständen (etwa alle 1000 Zeilen) löschen.

Es ist auch erwähnenswert, dass, wenn Ihre Datenbank nicht auf demselben Server ist, jede von Ihnen gestellte Abfrage über das Netzwerk an die Datenbank und das Ergebnis zurück zum Anwendungsserver gehen muss, bevor das Programm fortgesetzt werden kann. Abhängig von der Netzwerklatenz kann dies jedes Mal leicht eine Millisekunde dauern - und Sie tun dies mehrere Millionen Mal. Wenn es wirklich effizient sein muss, kann das Laden der gesamten Tabelle in den Speicher und das Durchführen der dort stattfindenden Prüfungen wesentlich schneller sein.

+0

Oh. Guter Aufruf, ich dachte, dass die Abfragen, die auf ein Commit warten, in einem temporären Speicherbereich in der Datenbank gespeichert wurden. Aber wenn sie auch in meiner Anwendung gespeichert werden, erklärt das, warum es langsamer wird und warum der Heap-Space explodiert. Ich werde versuchen, meine 'begin()' und 'commit()' zu kommentieren und sehen, ob es schneller geht :) –

+0

Also, wie ich in meinem Edit gesagt habe, habe ich versucht, aber es hat nicht besser funktioniert: ( Die Lösung, die ich versuche zu implementieren, beruht darauf, jede Datei mit +5000 Zeilen in Stücke zu schneiden und kleine Dateien zu bearbeiten. Wir werden sehen, ob es besser wird –

0

Problem "gelöst" (fast)! Ich habe meine persistence.xml so konfiguriert:

<property name="eclipselink.jdbc.batch-writing" value="JDBC"/> 
<property name="eclipselink.jdbc.batch-writing.size" value="10000"/> 

Zuerst es nichts ändern. Aber dann habe ich versucht, meine Datei in kleinere Stücke zu schneiden (wenn die Datei mehr als 5000 Zeilen hat, lese ich die 5000 Zeilen, ich speichere sie in einem StringBuilder, dann lese ich den StringBuilder, um 5000 Zeilen gleichzeitig einzufügen). So wird mein Code nach 20.000 Zeilen (vorerst) nicht langsamer. Es scheint gut zu funktionieren, aber ich kann immer noch nicht, warum mein Code immer langsamer, als ich mit größeren Stücken Datei ...

arbeitete

Vielen Dank an alle, die versuchten, mich auf diesen einen helfen;)