IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

Erreur "OutOfMemoryError: Java heap space" lors du cryptage AES d'un gros fichier


Sujet :

Langage Java

  1. #1
    Membre du Club
    Profil pro
    Administrateur réseau
    Inscrit en
    Mai 2009
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Administrateur réseau

    Informations forums :
    Inscription : Mai 2009
    Messages : 58
    Points : 41
    Points
    41
    Par défaut Erreur "OutOfMemoryError: Java heap space" lors du cryptage AES d'un gros fichier
    Bonjour,

    Dans mon application, je peux crypter les fichiers chargés avec l'algorithme de cryptage AES.

    Toute la partie de cryptage est exécutée dans un SwingWorker pour ne pas avoir l'interface graphique figée lors de l'opération de cryptage.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    public class Cryptage extends SwingWorker<Void, Void> {
     
    ...
     
    // Méthode exécuter en tâche de fond par le SwingWorker
        @Override
        protected Void doInBackground() throws Exception {
     
            // Message de lancement du cryptage de fichiers affiché dans la progressBar
            IHM.barre.setString("Cryptage en cours...");
     
            // Appel de la méthode permettant de crypter les fichiers chargés
            crypterFichiers();
     
            return null;
        }
     
        // Méthode exécutée automatiquement après doInBackground()
        public void done() {
     
                // Remonté d'erreurs possibles
                try { 
     
                    get(); 
     
                } catch (InterruptedException e) { 
     
                    System.out.println("InterruptedException :" + e.getMessage()); 
     
                } catch (ExecutionException e) { 
     
                    System.out.println("ExecutionException : " + e.getMessage());
                } 
     
                // Message de confirmation affiché dans la progressBar
                IHM.barre.setString("Cryptage terminé");
        }
     
    // Méthode permettant de crypter les fichiers chargés
        protected void crypterFichiers () {
     
            File tempFile = null;
     
            try {
     
                for (int i = 0; i < cheminFichiersRenommes.size(); i++) {
     
                    tempFile = new File(cheminFichiersRenommes.get(i));
     
                    byte[] data = new byte[(int) tempFile.length()];
     
                    // Génération de la clé 256bits utilisée par l'algorithme de cryptage AES
                    keyGen = KeyGenerator.getInstance("AES");
                    keyGen.init(256);
                    key = keyGen.generateKey();
     
                    /*
                    System.out.println("cle (" + key.getAlgorithm() + "," + key.getFormat()
                            + ") : " + new String(key.getEncoded()));
                    
                    System.out.println("Val hexa cle : " + bytesToHex(key.getEncoded()));
                    System.out.println("chemin fichier a crypter : " + cheminFichiersRenommes.get(i));
                    */
     
                    // Création de l'objet chiffreur
                    aesCipher = Cipher.getInstance("AES");
                    aesCipher.init(Cipher.ENCRYPT_MODE, key);
     
                    // Ouverture d'un flux d'écriture pour créer le fichier crypter
                    fos = new FileOutputStream(tempFile);
     
                    // Ouverture d'un flux de cryptage pour crypter le fichier
                    cos = new CipherOutputStream(fos, aesCipher);
     
                    // Cryptage du fichier
                    cos.write(data);
     
                    cos.close();
                    fos.close();
                }
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    Les fichiers chargés sont stockés dans un Vector de String.

    Je n'ai pas de problème lorsque le ou les fichiers à crypter ont une taille inférieur à 500Mo.

    Au-delà, j'ai un dépassement mémoire de la pile :
    ExecutionException : java.lang.OutOfMemoryError: Java heap space.
    Même en augmentant la taille maximale de la JVM (avec l’argument -Xmx), j'ai toujours le même problème.

    Quelqu'un aurait des pistes pour résoudre ce problème ?

    Merci d'avance pour votre aide.

  2. #2
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 545
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 545
    Points : 21 601
    Points
    21 601
    Par défaut
    Hello,

    Oui, par exemple tu pourrais éviter de créer des tableaux de bytes de la taille de tes fichiers. Forcément ça va consommer de la mémoire.

    À la place tu pourrais lire ton fichier par petits bouts et l'écrire dans CipherOutputStream par petits bouts.
    C'est en fait la seule et unique raison pour laquelle CipherOutputStream existe : éviter de charger toutes les données en mémoire, et les écrire par flux à mesure qu'on les lit d'un autre flux.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Membre du Club
    Profil pro
    Administrateur réseau
    Inscrit en
    Mai 2009
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Administrateur réseau

    Informations forums :
    Inscription : Mai 2009
    Messages : 58
    Points : 41
    Points
    41
    Par défaut
    Merci thelvin pour ta réponse.

    J'ai mis en place la lecture du fichier à crypter par bloc de 1024 octets, mais le soucis est que la méthode Read de FileInputStream me retourne -1 (indiquant la fin du fichier), alors que celui-ci est beaucoup plus gros que 1024 octets.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
     
     
    try {
     
    			for (int i = 0; i < cheminFichiersRenommes.size(); i++) {
     
    				tempFile = new File(cheminFichiersRenommes.get(i));
     
    				fis = new FileInputStream(tempFile);
     
    				System.out.println("Taille fis : " + fis.available());  // taille du fichier OK
     
    				// Génération de la clé 256bits utilisée par l'algorithme de cryptage AES
    				keyGen = KeyGenerator.getInstance("AES");
    				keyGen.init(256);
    				key = keyGen.generateKey();
     
    				/*
    				System.out.println("cle (" + key.getAlgorithm() + "," + key.getFormat()
    						+ ") : " + new String(key.getEncoded()));
     
    				System.out.println("Val hexa cle : " + bytesToHex(key.getEncoded()));
    				System.out.println("chemin fichier a crypter : " + cheminFichiersRenommes.get(i));
    				*/
     
     
    				// Création de l'objet chiffreur
    				aesCipher = Cipher.getInstance("AES");
    				aesCipher.init(Cipher.ENCRYPT_MODE, key);
     
    				// Ouverture d'un flux d'écriture pour créer le fichier crypter
    				fos = new FileOutputStream(tempFile);
     
    				// Ouverture d'un flux de cryptage pour crypter le fichier
    				cos = new CipherOutputStream(fos, aesCipher);
     
    				int octet = 0;
    				byte[] buffer = new byte[1024];
     
    				while ((octet = fis.read(buffer)) != -1) {  // me retourne -1
     
    					System.out.println("Val octet : " + octet);
     
    					// Cryptage du fichier
    					cos.write(buffer, 0, octet);
    				}
     
    				cos.close();
    				fos.close();
    				fis.close();
     
    			}	
     
    				} catch (NoSuchAlgorithmException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				} catch (NoSuchPaddingException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				} catch (InvalidKeyException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				} catch (FileNotFoundException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				} catch (IOException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    Une idée du soucis qu'il y a ?

    Merci,

    kmtaz

  4. #4
    Membre du Club
    Profil pro
    Administrateur réseau
    Inscrit en
    Mai 2009
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Administrateur réseau

    Informations forums :
    Inscription : Mai 2009
    Messages : 58
    Points : 41
    Points
    41
    Par défaut
    Je crois avoir trouvé^^.

    Je pense que le problème vient du fait que j'ouvre un flux de lecture et d'écriture sur le même fichier.

    Comment peut-on crypter le fichier à la volée ? obligé de passer par un fichier temporaire ?

    Merci,

    kmtaz

  5. #5
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    oui

  6. #6
    Membre du Club
    Profil pro
    Administrateur réseau
    Inscrit en
    Mai 2009
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Administrateur réseau

    Informations forums :
    Inscription : Mai 2009
    Messages : 58
    Points : 41
    Points
    41
    Par défaut
    J'ai mis en place une solution en fonction de la taille du fichier à crypter.

    Si celui-ci est supérieur à 300Mo, alors je crypte le fichier à la volée par bloc de 1024 octets pour ne pas saturer la mémoire. Si le fichier est inférieur à 300Mo, je le charge en mémoire pour le crypter.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
     
    try {
     
    	for (int i = 0; i < cheminFichiersRenommes.size(); i++) {
     
    		// Génération de la clé 256bits utilisée par l'algorithme de cryptage AES
    		keyGen = KeyGenerator.getInstance("AES");
    		keyGen.init(256);
    		key = keyGen.generateKey();
     
    		// Création de l'objet chiffreur
    		aesCipher = Cipher.getInstance("AES");
    		aesCipher.init(Cipher.ENCRYPT_MODE, key);
     
     
    		tempFile = new File(cheminFichiersRenommes.get(i));
     
    		// Si la taille du fichier est supérieur à 300Mo
    		if (tempFile.length() > 314572800) {
     
    			int octet = 0;
    			byte[] buffer = new byte[1024];
     
    			// Ouverture d'un flux de lecture sur le fichier à crypter
    			fis = new FileInputStream(tempFile);
     
     
    			// Chemin du fichier créé lors de l'opération de cryptage
    			String encryptedFile = tempFile.getParent() + "\\" +  tempFile.getName().substring(0,tempFile.getName().lastIndexOf(".")) + i 
    					+ tempFile.getName().substring(tempFile.getName().lastIndexOf("."));
     
     
    			// Ouverture d'un flux d'écriture pour créer le fichier crypter
    			fos = new FileOutputStream(encryptedFile);
     
    			// Ouverture d'un flux de cryptage pour crypter le fichier
    			cos = new CipherOutputStream(fos, aesCipher);
     
     
    			// Tant que la fin du fichier n'est pas atteint
    			while ((octet = fis.read(buffer)) != -1) {
     
    				// Cryptage du fichier
    				cos.write(buffer, 0, octet);
    			}
     
     
    		}
    		else {
     
    			byte[] data = new byte[(int) tempFile.length()];
     
    			// Ouverture d'un flux d'écriture pour créer le fichier crypter
    			fos = new FileOutputStream(tempFile);
     
    			// Ouverture d'un flux de cryptage pour crypter le fichier
    			cos = new CipherOutputStream(fos, aesCipher);
     
    			cos.write(data);
    		}
     
    		cos.close();
    		fos.close();
     
    		if (fis != null) {
     
    			fis.close();
    		}
     
     
    	}	
     
    	} catch (NoSuchAlgorithmException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	} catch (NoSuchPaddingException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	} catch (InvalidKeyException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	} catch (FileNotFoundException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	} catch (IOException e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	}
    Merci à tous pour votre aide

    kmtaz

  7. #7
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par kmtaz Voir le message
    Si celui-ci est supérieur à 300Mo, alors je crypte le fichier à la volée par bloc de 1024 octets pour ne pas saturer la mémoire. Si le fichier est inférieur à 300Mo, je le charge en mémoire pour le crypter.
    Pourquoi s'embêter avec deux codes différents alors qu'un seul pourrait très bien faire l'affaire plus efficacement !!!

    En plus ton second code est faux puisque tu ne lit jamais le fichier : tu te contentes de créer un tableau de même taille...



    Au passage il y aurait beaucoup de chose à améliorer dans ton code :
    • Où sont définies toutes les variables ???? Plus tu éloignes la déclaration de l'utilisation, plus tu augmentes les chances d'utiliser inutilement de la mémoire.
    • Il faut utiliser le try-with-resource de Java 7 pour libérer les fichiers (ou un try/finally par ressource le cas échéant).
      Sinon tu augmentes les risques d'une fuite mémoire...
    • Un catch qui se contente d'un e.printStackTrace(), c'est généralement pas bon...


    a++

  8. #8
    Membre du Club
    Profil pro
    Administrateur réseau
    Inscrit en
    Mai 2009
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Administrateur réseau

    Informations forums :
    Inscription : Mai 2009
    Messages : 58
    Points : 41
    Points
    41
    Par défaut
    Je suis partie un peu dans tous les sens avec ma condition sur la taille du fichier je l'avoue^^.

    Merci pour tous tes conseils adiGuba, cela m'a permis de produire un code beaucoup plus claire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
     
     
    try {
     
    			for (int i = 0; i < cheminFichiersRenommes.size(); i++) {
     
    				// Génération de la clé 256bits utilisée par l'algorithme de cryptage AES
    				keyGen = KeyGenerator.getInstance("AES");
    				keyGen.init(256);
    				key = keyGen.generateKey();
     
    				// Création de l'objet chiffreur
    				aesCipher = Cipher.getInstance("AES");
    				aesCipher.init(Cipher.ENCRYPT_MODE, key);
     
     
    				tempFile = new File(cheminFichiersRenommes.get(i));
     
    				int octet = 0;
    				byte[] buffer = new byte[1024];
     
    				// Chemin du fichier créé lors de l'opération de cryptage
    				String encryptedFile = tempFile.getParent() + "\\" +  tempFile.getName().substring(0,tempFile.getName().lastIndexOf(".")) + i 
    							+ tempFile.getName().substring(tempFile.getName().lastIndexOf("."));
     
     
    				// try-with-resource de Java 7 les flux sont fermés automatiquement après le bloc try
    				// Ouverture d'un flux de lecture sur le fichier à crypter
    				try (FileInputStream fis = new FileInputStream(tempFile);
     
    					// Ouverture d'un flux d'écriture pour créer le fichier crypter
    					FileOutputStream fos = new FileOutputStream(encryptedFile);
     
    					// Ouverture d'un flux de cryptage pour crypter le fichier
    					CipherOutputStream cos = new CipherOutputStream(fos, aesCipher);
    				) {
     
    					// Tant que la fin du fichier n'est pas atteint
    					while ((octet = fis.read(buffer)) != -1) {
     
    						// Cryptage du fichier
    						cos.write(buffer, 0, octet);
    					}
    				}
     
    				// Suppression du fichier source
    				tempFile.delete();
     
    			}	
     
    				} catch (NoSuchAlgorithmException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				} catch (NoSuchPaddingException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				} catch (InvalidKeyException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				} catch (FileNotFoundException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				} catch (IOException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
     
     
    	}
    Merci,

    kmtaz

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 3
    Dernier message: 24/04/2008, 13h09
  2. [Info] Chargement image et Java heap space
    Par dazz_x dans le forum Langage
    Réponses: 9
    Dernier message: 11/09/2007, 12h51
  3. [jarsigner] java heap space
    Par GLDavid dans le forum Langage
    Réponses: 3
    Dernier message: 16/08/2006, 12h35
  4. Eclipse erreur : java.lang.OutOfMemoryError: Java heap space
    Par sderecourt dans le forum Eclipse Java
    Réponses: 1
    Dernier message: 14/04/2006, 12h28
  5. [ memoire ] Java Heap Space
    Par natha dans le forum Général Java
    Réponses: 10
    Dernier message: 23/01/2006, 12h15

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo