Windows se bloque pendant (ou après) l'exécution du code Java générant de nombreuses images


Je développe un code qui doit parfois générer beaucoup d'images. Le programme fonctionne parfaitement bien lorsque j'ai un nombre relativement faible d'images à générer, mais lorsque je dois générer des dizaines de milliers d'images, quelque chose d'étrange pourrait se produire.

À un moment donné, Windows semble suspendu pendant l'exécution du code pendant quelques minutes. Le gestionnaire des tâches affirme que l'application Java utilise 0% du processeur à ce moment-là. En fait chaque application qui essaie d'utiliser un la ressource du disque dur se bloque, mais les applications qui sont déjà ouvertes et ne nécessitent pas d'accès au disque dur semblent fonctionner.

Ce qui est plus étrange, ce comportement pourrait se produire même quelques secondes/minutes après la fin de mon programme. Mais parfois, cela n'arrive pas du tout.

Voici l'exemple simplifié:

public static void main(String[] args) {
    try {
        for (int i = 1; i <= 100; i++) {
            String dirName = "tmp/" + i + "/";
            File dir = new File(dirName);
            dir.mkdirs();
            //put only 200 file into one directory
            for (int j = 0; j < 200; j++) {
                drawImage(dirName, j);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private static void drawImage(String dirName, int j) 
                             throws FileNotFoundException, IOException {
    BufferedImage bi = new BufferedImage(512, 512, BufferedImage.TYPE_INT_ARGB);
    Graphics2D graphics = bi.createGraphics();

    //draw something on the image
    for (int k = 0; k < 10; k++)
        graphics.drawLine(k, 0, k*2, 512);
    BufferedImage tmpBI=new BufferedImage(512, 512, BufferedImage.TYPE_INT_ARGB);
    Graphics2D tmpGraphics = tmpBI.createGraphics();
    tmpGraphics.drawImage(bi, 0, 0, 512, 512, 0, 0, 512, 512, null);

    //write image to png
    FileOutputStream fos;
    fos = new FileOutputStream(new File(dirName + "img" + j + ".PNG"));
    ImageIO.write(tmpBI, "PNG", fos);
    fos.close();
}

Ma première supposition est qu'il y a des problèmes avec les gestionnaires de fichiers dans le système d'exploitation ou que mon code Java gère mal les fichiers.

La deuxième supposition est ce garbage collector fait des choses magiques que je ne comprends pas.

Mais pour être honnête, je ne sais pas comment savoir quel est le vrai problème et comment le résoudre.

J'exécute le code sur Windows 7 64 bits et jdk1.7 64 bits avec le système de fichiers NTFS.


Mise à JOUR

Peu de réponses ont proposé des solutions de contournement. Je les ai tous testés avec le même effet:

  • changer le répertoire de sortie en clé USB
  • fil supplémentaire pour calculs
  • fichier ZIP unique comme flux de sortie pour tous les fichiers

Le dernier essai m'a surpris. Je m'attendais à ce que dans ce cas, il ne pend pas. J'ai donc effectué un autre test et au lieu d'écrire dans un fichier, j'ai utilisé NullOutputStream. Le résultat était le même...

Ma conclusion: soit il y a quelque chose qui ne va pas dans la bibliothèque swing (très peu fiable), soit il y a peut-être quelque chose qui ne va pas avec mon ordinateur/système d'exploitation. Je vais le vérifier sur d'autres ordinateurs / OS. Si le problème persiste je vais revenir à elle.

Author: gawi, 2014-03-21

3 answers

Le code que vous écrivez accède à certaines fonctions du noyau.

Les fonctions open et close pour OutputStream sont partiellement effectuées en natif, de même que createGraphics().

Lorsque vous accédez à des fonctions natives, gardez à l'esprit qu'elles auront leur propre synchronisation implémentée.

Je crois que vous êtes dans le scénario où la quantité d'informations envoyées au thread natif arrive à un rythme beaucoup plus élevé que le thread natif écrit sur le disque (quand vous y pensez, l'accès à la mémoire RAM est beaucoup plus rapide que la mémoire HDD).

Même si votre machine Java se ferme, les "tâches" ont déjà été données au thread natif pour écrire. C'est probablement pourquoi toutes les opérations sur disque dur sont suspendues.

Essayez d'ajouter un Thread Sleep à votre thread afin de mettre le thread Java dans une priorité inférieure et voir si cela aide.

En règle générale, la création de fichiers est une opération très coûteuse.20000 fichiers est même difficile d'être répertorié dans un dossier:).

 0
Author: MichaelCMS, 2014-03-21 13:59:42

Mon sentiment est que vous devriez invoquer les dessins sur le thread de l'interface utilisateur, car vous utilisez awt.Graphiques et BufferedImage. Ce sont des composants spécifiques à l'interface utilisateur et vous souhaitez toujours effectuer des opérations d'interface utilisateur sur le thread d'interface utilisateur.

Essayez quelque chose comme:

 EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    // do your graphics related operations here
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
 0
Author: Luiza U, 2014-03-21 14:13:20

Essayez de passer en multithread, afin de ne pas utiliser votre thread principal. Je suppose que ce code est à l'origine exécuté sous une forme swing autre que la console, si vous effectuez des opérations massives sur le thread principal, le programme ne sera pas redessiné jusqu'à la fin de l'opération.

public static void main(String[] args) {
     Thread operation = new Thread(new Runnable(ThreadOperation));
     operation.start();
}

public static void ThreadOperation() {
    try {
        for (int i = 1; i <= 100; i++) {
            String dirName = "tmp/" + i + "/";
            File dir = new File(dirName);
            dir.mkdirs();
            //put only 200 file into one directory
            for (int j = 0; j < 200; j++) {
                drawImage(dirName, j);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Vous pouvez également suivre la progression avec une barre des tâches avec un peu plus de codage.

Maintenant, vous devriez vraiment vous attendre à ce que d'autres programmes se bloquent pendant que votre programme enregistre une quantité massive de données sur le disque, car les disques durs ne sont pas aussi rapides que votre processeur peut être. Sauf si (ou même si) vous avez un SSD HD, vous aurez ce problème.

D'autres programmes suspendus pendant que votre processus écrit vos données, signifie seulement qu'ils essaient d'écrire des données sur le disque en utilisant leur thread principal et ne redessineront pas jusqu'à ce qu'ils aient fini d'écrire leurs données, alors Windows pense: Attendez ce gars ne redessine pas... doit être mort.

PS: Ce code pourrait être faux quelque part, j'ai codé beaucoup trop en c# ces derniers temps.

 0
Author: Felype, 2014-03-21 19:02:55