32 votes

Hémorragie de mémoire massive qui fait passer la taille du tas d'environ 64 Mo à 1,5 Go en environ 8 secondes. Un problème avec le ramasse-miettes?

Voici le problème:

enter image description here

Comme vous pouvez le voir, l'utilisation de la mémoire de ballons hors de contrôle! J'ai dû ajouter des arguments à la JVM pour augmenter la heapsize juste pour éviter des erreurs de mémoire insuffisante alors que je comprendre ce qu'il se passe. Pas bon!!!

Base Résumé de la Demande (pour le contexte)

Cette application est (finalement) va être utilisé pour la base de l'écran sur le CV et le modèle correspondant à des activités à des fins d'automatisation. Je veux atteindre le plus haut niveau de fréquence d'image possible pour regarder l'écran, et gérer l'ensemble de la transformation par l'intermédiaire d'une série de séparer la consommation de threads.

J'ai rapidement découvert que le stock Robot de classe est vraiment terrible de vitesse sage, donc j'ai ouvert la source, a pris toutes les évite la duplication des efforts et le gaspillage des frais généraux, et de reconstruit comme ma propre classe appelée FastRobot.

La Classe' Code:

public class FastRobot {
    private Rectangle screenRect;
    private GraphicsDevice screen;
    private final Toolkit toolkit;
    private final Robot elRoboto;
    private final RobotPeer peer;
    private final Point gdloc;
    private final DirectColorModel screenCapCM;
    private final int[] bandmasks;

    public FastRobot() throws HeadlessException, AWTException {
        this.screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
        this.screen = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
        toolkit = Toolkit.getDefaultToolkit();
        elRoboto = new Robot();
        peer = ((ComponentFactory)toolkit).createRobot(elRoboto, screen);

        gdloc = screen.getDefaultConfiguration().getBounds().getLocation();
        this.screenRect.translate(gdloc.x, gdloc.y);

        screenCapCM = new DirectColorModel(24,
                /* red mask */    0x00FF0000,
                /* green mask */  0x0000FF00,
                /* blue mask */   0x000000FF);
        bandmasks = new int[3];
        bandmasks[0] = screenCapCM.getRedMask();
        bandmasks[1] = screenCapCM.getGreenMask();
        bandmasks[2] = screenCapCM.getBlueMask();

        Toolkit.getDefaultToolkit().sync();
    }

    public void autoResetGraphicsEnv() {
        this.screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
        this.screen = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
    }

    public void manuallySetGraphicsEnv(Rectangle screenRect, GraphicsDevice screen) {
        this.screenRect = screenRect;
        this.screen = screen;
    }


    public BufferedImage createBufferedScreenCapture(int pixels[]) throws HeadlessException, AWTException {
//      BufferedImage image;
        DataBufferInt buffer;
        WritableRaster raster;

        pixels = peer.getRGBPixels(screenRect);
        buffer = new DataBufferInt(pixels, pixels.length);

        raster = Raster.createPackedRaster(buffer, screenRect.width, screenRect.height, screenRect.width, bandmasks, null);
        return new BufferedImage(screenCapCM, raster, false, null);
    }

    public int[] createArrayScreenCapture() throws HeadlessException, AWTException {
            return peer.getRGBPixels(screenRect);
    }

    public WritableRaster createRasterScreenCapture(int pixels[]) throws HeadlessException, AWTException {
    //  BufferedImage image;
        DataBufferInt buffer;
        WritableRaster raster;

        pixels = peer.getRGBPixels(screenRect);
        buffer = new DataBufferInt(pixels, pixels.length);

        raster = Raster.createPackedRaster(buffer, screenRect.width, screenRect.height, screenRect.width, bandmasks, null);
    //  SunWritableRaster.makeTrackable(buffer);
        return raster;
    }
}

En substance, tout ce que j'ai modifié à partir de l'original est le déplacement de plusieurs des allocations à partir de la fonction des organes, et de les définir comme des attributs de la classe de sorte qu'ils ne sont pas appelée à chaque fois. Faire cela eu une importante incidence sur les taux de trame. Même sur mon gravement sous alimenté ordinateur portable, il est passé de ~4 fps avec le Robot de la classe, à ~30 fps avec mon FastRobot classe.

Premier Test:

Quand j'ai commencé à outofmemory des erreurs dans mon programme principal, j'ai monté cette épreuve très simple de garder un œil sur la FastRobot. Note: ceci est le code qui produit le tas profil ci-dessus.

public class TestFBot {
    public static void main(String[] args) {
        try {
            FastRobot fbot = new FastRobot();

            double startTime = System.currentTimeMillis();
            for (int i=0; i < 1000; i++)
                fbot.createArrayScreenCapture();
            System.out.println("Time taken: " + (System.currentTimeMillis() - startTime)/1000.);

        } catch (AWTException e) {
            e.printStackTrace();
        }

    }
}

Examinés:

Il ne fait pas cela tous les temps, ce qui est vraiment étrange (et frustrant!). En fait, il le fait rarement à tous avec le code ci-dessus. Toutefois, la question de la mémoire devient facilement reproductible si j'ai plusieurs boucles for dos à dos.

Test 2

public class TestFBot {
    public static void main(String[] args) {
        try {
            FastRobot fbot = new FastRobot();

            double startTime = System.currentTimeMillis();
            for (int i=0; i < 1000; i++)
                fbot.createArrayScreenCapture();
            System.out.println("Time taken: " + (System.currentTimeMillis() - startTime)/1000.);

            startTime = System.currentTimeMillis();
            for (int i=0; i < 500; i++)
                fbot.createArrayScreenCapture();
            System.out.println("Time taken: " + (System.currentTimeMillis() - startTime)/1000.);

            startTime = System.currentTimeMillis();
            for (int i=0; i < 200; i++)
                fbot.createArrayScreenCapture();
            System.out.println("Time taken: " + (System.currentTimeMillis() - startTime)/1000.);

            startTime = System.currentTimeMillis();
            for (int i=0; i < 1500; i++)
                fbot.createArrayScreenCapture();
            System.out.println("Time taken: " + (System.currentTimeMillis() - startTime)/1000.);


        } catch (AWTException e) {
            e.printStackTrace();
        }

    }
}

Examiné

Le contrôle de segment de mémoire est maintenant reproductible je dirais environ 80% du temps. J'ai regardé tout bien que le générateur de profils, et la chose de la plupart remarque (je pense) est que le garbage collector apparemment arrêts de droit comme la quatrième et dernière boucle commence.

Le format de sortie le code ci-dessus a donné les horaires suivants:

Time taken: 24.282    //Loop1
Time taken: 11.294    //Loop2
Time taken: 7.1       //Loop3
Time taken: 70.739    //Loop4

Maintenant, si vous additionnez les trois premières boucles, il ajoute à 42.676, qui étrangement correspond au moment exact où le garbage collector s'arrête, et la mémoire de clous.

enter image description here

Maintenant, c'est mon premier rodéo avec profilage, pour ne pas mentionner la première fois que j'ai jamais même pensé à propos de la collecte des ordures, c'était toujours quelque chose qui fonctionne comme par magie dans l'arrière-plan -- donc, je ne suis pas sûr de ce que, si quelque chose, je l'ai découvert.

Autres Informations Sur Votre Profil

enter image description here

Augusto a suggéré de regarder le profil de la mémoire. Il y a 1500+ int[] qui sont répertoriés comme "inaccessible, mais pas encore recueillis." Ce sont certainement l' int[] tableaux que l' peer.getRGBPixels() crée, mais pour une raison quelconque, ils ne sont pas détruits. Ce complément d'info, malheureusement, ne fait qu'ajouter à ma confusion, que je ne suis pas sûr pourquoi, la GC ne serait pas la collecte des


Profil à l'aide de petits tas argument -Xmx256m:

Au irreputable et Chaude Lèche suggestion j'ai mis le max de la taille du segment de quelque chose de beaucoup plus petite. Bien que cela ne l'empêcher de faire le 1 go de sauter dans l'utilisation de la mémoire, il n'explique toujours pas pourquoi le programme est en montgolfière à son maximum, taille du tas à l'entrée de la 4ème itération.

enter image description here

Comme vous pouvez le voir, la cause exacte existe toujours, c'est juste plus petit. ;) Le problème avec cette solution est que le programme, pour une raison quelconque, est toujours de manger à travers la totalité de la mémoire, il peut -- il y a aussi un changement marqué dans les fps de la performance à partir de la première itérations, qui consomme très peu de mémoire, et la version finale, qui consomme plus de mémoire que peut.

La question reste de savoir pourquoi est-il de la montgolfière à tous?


Résultats après avoir atteint une Force de "Garbage Collection" bouton:

Au jtahlborn de la suggestion, j'ai frappé à la Force de la Collecte des Ordures bouton. Il a fonctionné à merveille. Il va de 1go de l'utilisation de la mémoire, jusqu'à la basline de 60 mo environ.

enter image description here

Donc, cela semble être le remède. Maintenant, la question est, comment puis-je pro grammaticalement de la force de la GC pour ce faire?


Résultats après l'ajout de locaux par les Pairs de la fonction de la portée:

À la David Eaux suggestion, j'ai modifié le createArrayCapture() fonction de sorte qu'il est titulaire d'un local Peer objet.

Malheureusement, aucun changement dans l'utilisation de la mémoire de modèle.

enter image description here

Obtient quand même énorme sur la 3ème ou 4ème itération.


La Mémoire De La Piscine De L'Analyse:

Les captures d'écran des différents pools de mémoire

Toutes les piscines:

enter image description here

Eden Piscine:

enter image description here

Old Gen:

enter image description here
Juste au sujet de tous les de l'utilisation de la mémoire semble tomber dans cette piscine.

Remarque: PS Survivant de l'Espace avait (apparemment) 0 utilisation


Je suis parti avec plusieurs questions:

(a) la Poubelle Profiler graphique de dire ce que je pense que cela signifie? Ou je confonds corrélation avec le lien de causalité? Comme je l'ai dit, je suis dans une région inconnue de ces questions.

(b) Si c' est le garbage collector... que dois-je faire à ce sujet..? Pourquoi est-il cesser, et de courir à un taux réduit pour le reste du programme?

(c) Comment puis-je résoudre ce problème?

Quelqu'un aurait-il une idée de ce qui se passe ici?

4voto

drone.ah Points 745

Essayez de spécifier un garbage collector manuellement.

Simultanées Marque de Balayage est un but d'intérêt général qui fournit un bon équilibre entre la basse-pause et de débit raisonnable.

Si vous êtes sur Java 7 ou une version ultérieure de Java 6, G1 collecteur est probablement mieux depuis qu'il est en mesure de prévenir la fragmentation de la mémoire.

Vous pouvez vérifier la version Java SE Hotspot de la Machine Virtuelle de Collecte des Ordures de Paramétrage de la page pour plus d'informations et des pointeurs :-D

3voto

David Waters Points 6645

Vous avez dit que vous avez déplacé la création d'objet à partir de méthodes pour des champs de la classe. A été l'une des dépendances vous avez déplacé "par les pairs"? par exemple

peer = ((ComponentFactory)toolkit).createRobot(elRoboto, screen);

Il se pourrait bien que le peer détient toutes les captures d'écran prises pour la durée de vie de l'objet, cela permettrait d'obtenir effacée lorsque les pairs s'est déplacé hors de portée, la fin de la méthode dans le Robot, la vie de la classe pour FastRobot.

Essayez de déplacer la création et de la portée par des pairs à l'arrière de votre méthode et de voir quelle est la différence.

public int[] createArrayScreenCapture() throws HeadlessException, AWTException {
        RobotPeer localPeer = ((ComponentFactory)toolkit).createRobot(elRoboto, screen);
        return localPeer.getRGBPixels(screenRect);
}

Tentative 2

Donc, cela semble être le remède. Maintenant, la question est, comment puis-je pro grammaticalement, la force de la GC pour ce faire?

Vous pouvez appeler le Système.gc() pour demander un garbage collection. Note ceci est une demande, pas une demande. La JVM sera exécuté uniquement de la collecte des ordures s'il pense que sa vaut la peine alors que.

Comme vous pouvez le voir, la cause exacte existe toujours, il vient d'être fait les plus petits. ;) Le problème avec cette solution est que le programme, pour certains la raison, est toujours de manger à travers la totalité de la mémoire, il peut -- il est également un changement marqué dans les fps de la performance à partir de la première itérations, qui consomme très peu de mémoire, et la version finale, qui consomme plus de mémoire que peut.

La question reste de savoir pourquoi est-il de la montgolfière à tous?

La JVM va essayer et seulement exécuter une grande Collecte des Ordures lorsque c'est absolument nécessaire (plus de mémoire utilisée). ( Lu dans la Jeune Génération vs Ancienne Génération et au sein de la Jeune Génération, l'Eden de l'espace et survivant de l'espace). Attendez-vous donc à longue durée de vie ou la mémoire de la faim des applications java à s'asseoir près de la taille maximale du tas. Il est intéressant de noter que pour la mémoire de se déplacer dans l'Ancienne Génération, il faut que ça survécu 3 minor GC fonctionne (Eden => Survivor 1 => Survivor 2 => Ancienne Génération [Selon ce JVM vous êtes en cours d'exécution et que GC régime vous avez selcted sur les arguments de ligne de commande.])

Quant à savoir pourquoi ce changement de comportement, il pourrait être n'importe quel nombre de choses. Cette dernière boucle est la plus longue, n'Système.getCurrentTimeMillis() bloc pour assez longtemps pour que le GC à avoir un aller sur un autre fil? Donc, le problème ne montre en plus les boucles? Le processus de prendre une capture d'écran de son niveau assez bas pour moi, je suppose mis en œuvre avec appel à la operatating système de kernel, est-ce à bloquer le processus en kernel space prévenir les autres threads en cours d'exécution? (ce qui pourrait arrêter le gc cours d'exécution dans le thread d'arrière-plan).

Jetez un oeil à http://www.javacodegeeks.com/2012/01/practical-garbage-collection-part-1.html pour une introduction à l'univers de la collecte des ordures. Ou Java Mémoire expliqué (JVM de SUN) pour des tas d'autres liens.

L'espoir qui l'a aidé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X