Question

À ma place, où je travaille, j'avais des fichiers contenant plus de millions de lignes par fichier. Même si la mémoire du serveur fait plus de 10 Go avec 8 Go pour la machine virtuelle Java, le serveur est parfois suspendu pendant quelques instants et étouffe les autres tâches.

J'ai profilé le code et constaté que l'utilisation de la mémoire de lecture de fichier augmentait fréquemment en giga-octets (1 Go à 3 Go), puis revenait soudain à la normale. Il semble que cette utilisation fréquente de la mémoire haute et basse bloque mes serveurs. Bien sûr, cela était dû au ramassage des ordures.

Quelle API dois-je utiliser pour lire les fichiers pour de meilleures performances?

Bien, j'utilise BufferedReader (nouveau FileReader (...)) pour lire ces fichiers CSV.

Processus: comment lire le fichier?

  1. Je lis les fichiers ligne par ligne.
  2. Chaque ligne a peu de colonnes. en fonction des types que je les analyse en conséquence (colonne de coût en double, colonne de visite en int, colonne de mot clé en chaîne, etc.).
  3. Je place le contenu éligible (visite > 0) dans une carte de hachage, puis efface cette carte à la fin de la tâche

Mettre à jour

Je fais cette lecture de 30 ou 31 fichiers (données d'un mois) et stocke les fichiers éligibles dans une carte. Plus tard, cette carte est utilisée pour obtenir des coupables dans différents tableaux. Par conséquent, la lecture est indispensable et le stockage de ces données est également indispensable. Bien que j’ai maintenant remplacé la partie HashMap par BerkeleyDB, le problème au moment de la lecture du fichier est identique, voire pire.

Était-ce utile?

La solution

BufferedReader est l’une des deux meilleures API à utiliser pour cela. Si vous rencontrez vraiment des problèmes de lecture de fichier, vous pouvez également utiliser les éléments NIO pour mapper en mémoire vos fichiers, puis lisez le contenu directement hors de la mémoire.

Mais votre problème n’est pas avec le lecteur. Votre problème est que chaque opération de lecture crée une série de nouveaux objets, le plus souvent dans ce que vous faites juste après la lecture.

Vous devriez envisager de nettoyer votre traitement d'entrée en veillant à réduire le nombre et / ou la taille des objets que vous créez, ou tout simplement à vous débarrasser plus rapidement des objets dès qu'ils ne sont plus nécessaires. Serait-il possible de traiter votre fichier ligne par ligne ou morceau à la fois plutôt que d’inhaler le tout en mémoire pour le traitement?

Une autre possibilité serait de jouer avec le ramassage des ordures. Vous avez deux mécanismes:

  • Appelez explicitement le ramasse-miettes de temps en temps, par exemple toutes les 10 secondes ou toutes les 1 000 lignes d'entrée. Cela augmentera la quantité de travail effectué par le GC, mais cela prendra moins de temps pour chaque GC, votre mémoire ne gonflera pas autant et nous espérons donc que l'impact sur le reste du serveur sera moindre.

  • Violonnez avec les options du ramasse-miettes de la JVM. Celles-ci diffèrent d’une machine à l’autre, mais java -X devrait vous donner quelques conseils.

Mise à jour: L'approche la plus prometteuse:

Avez-vous vraiment besoin de l'ensemble du jeu de données en mémoire en même temps pour le traitement?

Autres conseils

  

J'ai profilé le code et constaté que   tandis que l'utilisation de la mémoire de lecture de fichier augmente   Giga-octets fréquemment (1 Go à 3 Go) et   puis revient soudainement à la normale. Il   semble que cette fréquente haute et basse   La mémoire utilise accroche mes serveurs. De   Bien sûr, cela était dû à Garbage   collection.

L'utilisation de BufferedReader (new FileReader (...)) ne causera pas cela.

Je soupçonne que le problème est que vous lisez les lignes / lignes dans un tableau ou une liste, les traitez puis les supprimez. Cela entraînera une augmentation de l'utilisation de la mémoire, puis une nouvelle diminution. Si tel est le cas, vous pouvez réduire l'utilisation de la mémoire en traitant chaque ligne / ligne au fur et à mesure de sa lecture.

MODIFIER : nous convenons que le problème concerne l'espace utilisé pour représenter le contenu du fichier en mémoire. Une alternative à une énorme table de hachage en mémoire est de revenir à l’ancien & sort; sort fusion " approche utilisée lorsque la mémoire de l’ordinateur était mesurée en kilo-octets. (Je suppose que le traitement est dominé par une étape dans laquelle vous effectuez une recherche avec les touches K pour obtenir la ligne R associée.)

  1. Si nécessaire, prétraitez chacun des fichiers d'entrée afin qu'ils puissent être triés sur la clé K.

  2. Utilisez un utilitaire de tri de fichiers efficace pour trier tous les fichiers d'entrée dans l'ordre sur le K. Vous souhaitez utiliser un utilitaire qui utilisera un algorithme de tri de fusion classique. Cette volonté divisez chaque fichier en morceaux plus petits pouvant être triés en mémoire, triez-les, écrivez-les dans des fichiers temporaires, puis fusionnez les fichiers temporaires triés. L’utilitaire sort UNIX / Linux est une bonne option.

  3. Lisez les fichiers triés en parallèle, en lisant toutes les lignes relatives à chaque valeur de clé, en les traitant puis en passant à la valeur de clé suivante.

En fait, je suis un peu surpris que l'utilisation de BerkeleyDB n'ait pas aidé. Cependant, si le profilage vous indique que la construction de la base de données a pris beaucoup de temps, vous pourrez peut-être l'accélérer en triant le fichier d'entrée (comme ci-dessus!) Dans l'ordre croissant des clés avant de générer la base de données. (Lorsque vous créez un index basé sur des fichiers volumineux, vous obtenez de meilleures performances si les entrées sont ajoutées dans l'ordre des clés.)

Essayez d’utiliser les options de vm suivantes afin d’ajuster le gc (et de faire une impression gc):

-verbose:gc -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top