J'apprends toujours les ficelles de Java donc désolé s'il y a une réponse évidente à cela. J'ai un programme qui prend une tonne de mémoire et je veux trouver un moyen de réduire son utilisation , mais après avoir lu de nombreuses questions SO, j'ai l'idée que je dois prouver où est le problème avant de commencer à l'optimiser.
Alors, voici ce que j'ai fait, j'ai ajouté un point d'arrêt au début de mon programme et l'ai exécuté, puis j'ai démarré visualVM et l'ai fait profiler la mémoire (j'ai aussi fait la même chose dans les netbeans juste pour comparer les résultats et ce sont les mêmes ). Mon problème est que je ne sais pas les lire, j'ai obtenu la zone la plus élevée en disant simplement char[]
et je ne vois aucun code ni rien (ce qui est logique car visualvm se connecte au jvm et ne peut pas voir ma source, mais netbeans ne me montre pas non plus la source comme il le fait lors du profilage cpu).
Fondamentalement, ce que je veux savoir, c'est quelle variable (et j'espère plus de détails comme dans quelle méthode) toute la mémoire est utilisée afin que je puisse me concentrer sur mon travail. Existe-t-il un moyen simple de procéder? En ce moment, j'utilise Eclipse et Java pour développer (et j'ai installé visualVM et netbeans spécifiquement pour le profilage, mais je suis prêt à installer tout ce qui, selon vous, fait ce travail).
EDIT: Idéalement, je cherche quelque chose qui va prendre tous mes objets et les trier par taille (afin que je puisse voir lequel monopolise la mémoire). Actuellement, il renvoie des informations génériques telles que string [] ou int [] mais je veux savoir à quel objet il fait référence afin que je puisse travailler à obtenir sa taille plus optimisée.
Les chaînes sont problématiques
Fondamentalement, en Java, String
les références (les choses qui utilisent char[]
Dans les coulisses) domineront la plupart des applications métier en termes de mémoire . La façon dont ils sont créés détermine la quantité de mémoire qu'ils consomment dans la machine virtuelle Java.
Tout simplement parce qu'ils sont si fondamentaux pour la plupart des applications d'entreprise en tant que type de données, et ils sont également l'un des plus gourmands en mémoire. Ce n'est pas seulement une chose Java chose, String
les types de données prennent beaucoup de mémoire dans à peu près toutes les langues et bibliothèques d'exécution, car au moins ce ne sont que des tableaux de 1 octet par caractère ou au pire (Unicode) ce sont des tableaux de plusieurs octets par caractère.
Une fois, lors du profilage de l'utilisation du processeur sur une application Web qui avait également une dépendance Oracle JDBC, j'ai découvert que StringBuffer.append()
dominait les cycles du processeur de plusieurs ordres de grandeur sur tous les autres appels de méthode combiné, beaucoup moins tout autre appel de méthode unique. Le pilote JDBC a fait beaucoup et beaucoup de manipulation de String
, sorte de compromis sur l'utilisation de PreparedStatements
pour tout.
Ce qui vous inquiète, vous ne pouvez pas le contrôler, pas directement de toute façon
Ce sur quoi vous devez vous concentrer, c'est ce que vous contrôlez, c'est-à-dire vous assurer de ne pas conserver les références plus longtemps que nécessaire et de ne pas dupliquer les choses inutilement. Les routines de récupération de place dans Java sont hautement optimisées, et si vous apprenez comment fonctionnent leurs algorithmes, vous pouvez vous assurer que votre programme se comporte de la manière optimale pour que ces algorithmes fonctionnent.
Java Heap Memory n'est pas comme la mémoire gérée manuellement dans d'autres langues, ces règles ne s'appliquent pas
Ce qui est considéré les fuites de mémoire dans d'autres langues ne sont pas la même chose/cause racine que dans Java avec ses ordures système de collecte.
Très probablement dans Java n'est pas consommée par un seul objet uber qui fuit (référence suspendue dans d'autres environnements).
Il s'agit probablement de beaucoup d'allocations plus petites en raison des objets StringBuffer
/StringBuilder
qui ne sont pas dimensionnés de manière appropriée lors des premières instanciations et qui doivent ensuite augmenter automatiquement les tableaux char[]
Pour contenir la fonction append()
appels.
Ces objets intermédiaires peuvent être conservés plus longtemps que prévu par le garbage collector en raison de l'étendue dans laquelle ils se trouvent et de beaucoup d'autres choses qui peuvent varier au moment de l'exécution.
EXEMPLE: le garbage collector peut décider qu'il y a des candidats, mais parce qu'il considère qu'il reste encore beaucoup de mémoire qu'il pourrait être trop coûteux en temps de les débusquer à ce moment-là , et il attendra que la pression de la mémoire augmente.
Le garbage collector est vraiment bon maintenant, mais ce n'est pas magique, si vous faites des choses dégénérées, cela ne fonctionnera pas de manière optimale. Il existe de nombreuses documentations sur Internet sur les paramètres du garbage collector pour toutes les versions des JVM.
Ces objets non référencés n'ont peut-être tout simplement pas atteint le moment où le garbage collector pense qu'il en a besoin pour qu'ils soient effacés de la mémoire, ou il pourrait y avoir des références à eux détenus par un autre objet (List
) pour exemple que vous ne réalisez pas pointe toujours vers cet objet. C'est ce qu'on appelle le plus souvent une fuite en Java, qui est une fuite de référence plus spécifiquement.
EXEMPLE: Si vous savez que vous devez construire un 4K String
en utilisant un StringBuilder
créez-le avec new StringBuilder(4096);
pas la valeur par défaut, qui est comme 32 et commencera immédiatement à créer des ordures qui peuvent représenter plusieurs fois ce que vous pensez que l'objet doit être sage.
Vous pouvez découvrir combien de quels types d'objets sont instanciés avec VisualVM, cela vous dira ce que vous devez savoir. Il n'y aura pas une seule grande lumière clignotante qui pointe vers une seule instance d'une seule classe qui dit: "C'est le grand consommateur de mémoire!", C'est-à-dire à moins qu'il n'y ait qu'une seule instance de certains char[]
que vous lisez un fichier volumineux, et ce n'est pas possible non plus, car beaucoup d'autres classes utilisent char[]
en interne; et vous le saviez déjà à peu près.
Je ne vois aucune mention de OutOfMemoryError
Vous n'avez probablement pas de problème dans votre code, le système de récupération de place n'est peut-être pas soumis à une pression suffisante pour lancer et désallouer des objets que vous pensez il devrait être en train de nettoyer. Ce que vous pensez est un problème ne l'est probablement pas, sauf si votre programme plante avec OutOfMemoryError
. Ce n'est pas C, C++, Objective-C ou tout autre langage/runtime de gestion manuelle de la mémoire. Vous ne pouvez pas décider de ce qui est en mémoire ou non au niveau de détail auquel vous vous attendez.
Dans JProfiler , vous pouvez accéder au marcheur de tas et activer la plus grande vue des objets. Vous verrez les objets qui conservent le plus de mémoire. La mémoire "conservée" est la mémoire qui serait libérée par le garbage collector si vous supprimiez l'objet.
Vous pouvez ensuite ouvrir les nœuds d'objet pour voir l'arborescence de référence des objets retenus. Voici une capture d'écran de la plus grande vue d'objet:
Avertissement: Mon entreprise développe JProfiler
Java JDK est livré avec JVisualVM sous le dossier bin, une fois que votre serveur d'applications (par exemple est en cours d'exécution), vous pouvez exécuter visualvm et le connecter à votre hôte local, qui vous fournir l'allocation de mémoire et vous permettre d'effectuer un vidage de tas
Pour des étapes plus détaillées sur la façon d'activer: http://sysdotoutdotprint.com/technologies/Java/6
Je recommanderais de capturer des vidages de tas et d'utiliser un outil comme Eclipse MAT qui vous permet de les analyser. Il existe de nombreux tutoriels disponibles. Il fournit une vue de arbre de dominateur pour donner un aperçu des relations entre les objets sur le tas. Spécifiquement pour ce que vous avez mentionné, la fonction "chemin vers les racines GC" de MAT vous indiquera où la majorité de ces objets char [], String [] et int [] sont référencés. JVisualVM peut également être utile pour identifier les fuites et les allocations, notamment en utilisant des instantanés avec des traces de pile d'allocation. Il y en a pas mal pas à pas du processus pour obtenir les instantanés et les comparer pour trouver le point d'allocation.
Si vous utilisez visualVM pour vérifier votre utilisation de la mémoire, il se concentre sur les données, pas sur les méthodes. Peut-être que vos données big char [] sont causées par de nombreuses valeurs de chaîne? Sauf si vous utilisez la récursivité, les données ne proviendront pas de variables locales. Vous pouvez donc vous concentrer sur les méthodes qui insèrent des éléments dans de grandes structures de données. Pour savoir quelles déclarations précises provoquent votre "fuite de mémoire", je vous suggère en plus