2009-09-01 4 views
4

Je travaille avec Lucene 2.4.0 et la JVM (JDK 1.6.0_07). Je reçois constamment OutOfMemoryError: Java heap space, en essayant d'indexer de gros fichiers texte. Exemple 1: L'indexation d'un fichier texte de 5 Mo manque de mémoire avec un maximum de 64 Mo. taille du tas. J'ai donc augmenté le maximum. Taille du tas à 512 Mo. Cela a fonctionné pour le fichier texte de 5 Mo, mais Lucene utilisait toujours 84 Mo d'espace de tas pour le faire. Pourquoi tant?Pourquoi Lucene provoque-t-il OOM lors de l'indexation de fichiers volumineux?

La classe FreqProxTermsWriterPerField semble être de loin le plus gros consommateur de mémoire selon JConsole et le plugin TPTP Memory Profiling pour Eclipse Ganymede.

Exemple 2: L'indexation d'un fichier texte de 62 Mo est à court de mémoire avec un maximum de 512 Mo. taille du tas. Augmenter le max. La taille du tas à 1024 Mo fonctionne mais Lucene utilise 826 Mo d'espace de tas tout en effectuant cela. Il semble encore que trop de mémoire soit utilisée pour faire cela. Je suis sûr que les fichiers plus volumineux causeraient l'erreur car elle semble corrélative.

Je suis sur une plate-forme Windows XP SP2 avec 2 Go de RAM. Alors, quelle est la meilleure pratique pour l'indexation de gros fichiers? Voici un extrait de code que j'utilise:

// Index the content of a text file. 
private Boolean saveTXTFile(File textFile, Document textDocument) throws MyException {   

     try {    

       Boolean isFile = textFile.isFile(); 
       Boolean hasTextExtension = textFile.getName().endsWith(".txt"); 

       if (isFile && hasTextExtension) { 

        System.out.println("File " + textFile.getCanonicalPath() + " is being indexed"); 
        Reader textFileReader = new FileReader(textFile); 
        if (textDocument == null) 
          textDocument = new Document(); 
        textDocument.add(new Field("content", textFileReader)); 
        indexWriter.addDocument(textDocument); // BREAKS HERE!!!! 
       }      
     } catch (FileNotFoundException fnfe) { 
       System.out.println(fnfe.getMessage()); 
       return false; 
     } catch (CorruptIndexException cie) { 
       throw new MyException("The index has become corrupt."); 
     } catch (IOException ioe) { 
       System.out.println(ioe.getMessage()); 
       return false; 
     }      
     return true; 
    } 
+0

Je trouve bizarre que FreqProxTermsWriterPerField devrait apparaître comme un gros consommateur. Lorsque vous utilisez le constructeur Field (String, Reader), comme vous l'avez fait, il ne stocke pas les vecteurs de terme. Pouvez-vous s'il vous plaît poster le code sur la façon dont vous avez initialisé IndexWriter, comment cette méthode est appelée et le post-traitement, le cas échéant. –

+0

Voici comment j'initialise l'IndexWriter: indexWriter = new IndexWriter (indexRépertoire, new StandardAnalyzer(), createFlag, MaxFieldLength.UNLIMITED); \t \t \t \t indexWriter.setMergeScheduler (nouveau org.apache.lucene.index.SerialMergeScheduler()); \t \t \t \t indexWriter.setRAMBufferSizeMB (32); indexWriter.setMergeFactor (1000); \t indexWriter.setMaxFieldLength (Integer.MAX_VALUE); indexWriter.setUseCompoundFile (false); indexWriter.Fermer(); –

+0

Désolé pour le formatage. Savez-vous comment je peux re-poster et obtenir les extraits de code pour ressembler à mon message original? –

Répondre

1

Le profilage est la seule façon de déterminer une telle consommation de mémoire.

Aussi, dans votre code, vous ne fermons pas les gestionnaires de fichiers, Indexreaders, Inderwriters, peut-être le coupable pour OOM,

+0

J'utilise le profilage JConsole et TPTP pour Eclipse. Lorsque j'essaie d'indexer le fichier de 5 Mo avec 64 Mo d'espace de tas maximum. Je manque de mémoire très rapidement. –

0

Vous pouvez régler la IndexWriter pour débusquer fonction de l'utilisation de la mémoire ou nombre de documents - I suggère de le paramétrer sur flsuh en fonction de la mémoire et de voir si cela résout votre problème. Je suppose que tout votre index vit en mémoire parce que vous ne le videz jamais sur le disque.

+0

Voici comment j'initialise l'IndexWriter: indexWriter = new IndexWriter (indexDirectory, nouveau StandardAnalyzer(), createFlag, MaxFieldLength.UNLIMITED); \t \t \t \t indexWriter.setMergeScheduler (nouveau org.apache.lucene.index.SerialMergeScheduler()); \t \t \t \t indexWriter.setRAMBufferSizeMB (32); // devrait-il tirer à droite? indexWriter.setMergeFactor (1000); \t indexWriter.setMaxFieldLength (Integer.MAX_VALUE); indexWriter.setUseCompoundFile (false); indexWriter.close(); –

+0

Désolé pour le formatage. Je ne savais pas que ça allait le faire. La ligne: indexWriter.setRAMBufferSizeMB (32) ... devrait flush lorsque 32 Mo d'espace tas est utilisé à droite? –

3

En réponse comme commentaire à Gandalf

Je peux vous voir réglez l'setMergeFactor 1000

l'API dit

setMergeFactor

public void setMergeFactor(int mergeFactor)

Determines how often segment indices are merged by addDocument(). With smaller values, less RAM is used while indexing, and searches on unoptimized indices are faster, but indexing speed is slower. With larger values, more RAM is used during indexing, and while searches on unoptimized indices are slower, indexing is faster. Thus larger values (> 10) are best for batch index creation, and smaller values (< 10) for indices that are interactively maintained.

Cette méthode est une méthode pratique, il utilise la RAM comme vous augmentez le mergeFactor

Ce que je suggérerais est réglé à quelque chose comme 15 ou ainsi de suite .; (sur une base d'essai et d'erreur) complétée avec setRAMBufferSizeMB, appelez également Commit(). then optimize() puis close() l'objet d'indexeur (créez probablement un JavaBean et mettez toutes ces méthodes dans une méthode) appelez cette méthode lorsque vous fermez l'index.

poste avec votre résultat, commentaires =]

+0

Merci pour la réponse. Je vais essayer quelques valeurs plus petites pour mergeFactor. J'ai eu recours à la pagination du fichier qui est très lent et qui atteint finalement un point de MOO. Bien sûr, si j'augmente l'espace de tas max de JVM, le fichier de 5 Mo sera indexé très rapidement. Cependant, je vais parfois indexer des fichiers très volumineux (250 Mo et plus) et je ne peux pas changer dynamiquement la taille du tas JVM. Après l'analyse comparative, il semble que toute taille de fichier représente 5% de l'espace mémoire nécessaire pour l'indexer en une fois. Malheureusement, je n'ai pas la RAM. Donc, à moins que quelqu'un ait une réponse, je vais essayer de faire fonctionner la pagination et la rendre aussi rapide. –

+0

Peu importe, ** setMergeFactor ** doit être défini de manière appropriée, nous avons indexé avec succès le jeu de résultats JDBC à l'échelle> 10 Go, avec des machines de développement ayant 2gigs RAM éq. pouvez-vous poster le code complet ici? – Narayan

+0

Naryan - voir mon code reposted. Merci. –

0

Nous avons connu quelques similaires « de mémoire » problèmes plus tôt cette année lors de la construction de nos index de recherche pour notre maven repository search engine at jarvana.com. Nous construisions les index sur une machine quad core Windows Vista 64 bits, mais nous utilisions Java 32 bits et Eclipse 32 bits. Nous avions 1,5 Go de RAM allouée à la JVM. Nous avons utilisé Lucene 2.3.2. L'application indexe environ 100 Go de données principalement compressées et nos index finissent par être d'environ 20 Go.

Nous avons essayé un certain nombre de choses, telles que vider l'IndexWriter, en appelant explicitement le garbage collector via System.gc(), essayant de déréférencer tout ce qui est possible, etc. Nous avons utilisé JConsole pour surveiller l'utilisation de la mémoire. Bizarrement, nous rencontrions souvent des erreurs "OutOfMemoryError: Java heap space" alors qu'elles n'auraient pas dû se produire, d'après ce que nous avions vu dans JConsole. Nous avons essayé de passer à différentes versions de Java 32 bits, et cela n'a pas aidé. Nous avons finalement opté pour Java 64 bits et Eclipse 64 bits. Lorsque nous l'avons fait, nos blocages de mémoire de segment lors de l'indexation ont disparu lors de l'exécution avec 1,5 Go alloués à la JVM 64 bits. De plus, le passage au Java 64 bits nous a permis d'allouer plus de mémoire à la JVM (nous sommes passés à 3Go), ce qui a accéléré notre indexation.

Vous ne savez pas exactement quoi suggérer si vous utilisez XP. Pour nous, nos problèmes OutOfMemoryError semblaient se rapporter à quelque chose à propos de Windows Vista 64 et 32 ​​bits Java. Peut-être passer à l'exécution sur une machine différente (Linux, Mac, Windows différent) pourrait aider. Je ne sais pas si nos problèmes ont disparu pour de bon, mais ils semblent avoir disparu pour l'instant.

2

Pour les utilisateurs d'hibernate (en utilisant mysql) et aussi en utilisant des grails (via un plugin interrogeable).

J'ai continué à recevoir des erreurs d'OOM lors de l'indexation de 3M lignes et de 5Go de données.

Ces paramètres semblent avoir résolu le problème sans que j'aie besoin d'écrire des indexeurs personnalisés.

voici quelques choses à essayer:

Paramètres de la boussole:

 'compass.engine.mergeFactor':'500', 
     'compass.engine.maxBufferedDocs':'1000' 

et mise en veille prolongée (pas sûr s'il est nécessaire, mais peut-être aider, esp w/mysql qui a un résultat jdbc continu désactivé par défaut. [texte du lien] [1]

 hibernate.jdbc.batch_size = 50 
     hibernate.jdbc.fetch_size = 30 
     hibernate.jdbc.use_scrollable_resultset=true 

en outre, il semble spécialement pour mysql, a dû ajouter des paramètres d'URL à la chaîne de connexion jdbc.

 url = "jdbc:mysql://127.0.0.1/mydb?defaultFetchSize=500&useCursorFetch=true" 

(mise à jour: avec les paramètres d'URL, la mémoire ne va pas au-dessus de 500 Mo)

En tout cas, maintenant, je suis en mesure de construire mon Lucene/index comapss avec moins de 2 Go taille du tas. Auparavant j'avais besoin de 8 Go pour éviter OOM. J'espère que cela aide quelqu'un.

[1]: http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html mysql le streaming jdbc

resultset
+0

Il s'avère, lors de la définition de useCursorFetch = true sur mysql, le jvm n'utilise pas beaucoup de mémoire, mais mysql écrit un fichier temporaire pour gérer la réponse mise en mémoire tampon. Pour quelque raison que ce soit sur ma machine, ce fichier dépassait les 50 Go et stoppait ma machine. Découvert que set entitiesIndexer = new PaginationHibernateIndexEntitiesIndexer() au lieu de la nouvelle par défaut ScrollableHibernateIndexEntitiesIndexer(); sur l'HibernateGpsDevice a l'indexeur casser les querries en petits lots de fetchCount. Maintenant, je peux indexer mes données sans utiliser trop de mémoire sur le côté jvm ou mysql. – user339047

Questions connexes