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

Java Discussion :

Java et la gestion de la mémoire


Sujet :

Java

  1. #1
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut Java et la gestion de la mémoire
    Bonjour tout le monde,

    Quelque chose m'échappe dans la gestion de la mémoire de Java. Je ne comprends pas pourquoi j'obtiens une OutOfMemoryError alors qu'il semble pourtant qu'il y ai assez de mémoire disponible.

    Voici un exemple de code générant un OutOfMemoryError (avec les paramètres Xms et Xmx par défaut) :
    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
    package testheapsize;
     
    import java.util.logging.Level;
    import java.util.logging.Logger;
     
    public class Main {
     
        public static void main(String... args) {
            try {
                long size = 178868493;
     
                Runtime runtime = Runtime.getRuntime();
                long free0 = runtime.freeMemory();
                System.out.println(size + " : " + free0 + " / " + runtime.totalMemory());
                byte[] fileBytes = new byte[(int) size];
                long free1 = runtime.freeMemory();
                System.out.println(size + " : " + free1 + " / " + runtime.totalMemory());
            } catch (Exception ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    Ce que j'obtiens donc est la chose suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    178868493 : 15964944 / 16252928
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
            at testheapsize.Main.main(Main.java:15)
    Java Result: 1
    Soit, pourquoi pas.

    Cependant, si au lieu de 178868493 je mets 178868492 (donc 1 octet de différence) :
    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
    package testheapsize;
     
    import java.util.logging.Level;
    import java.util.logging.Logger;
     
    public class Main {
     
        public static void main(String... args) {
            try {
                long size = 178868492;
     
                Runtime runtime = Runtime.getRuntime();
                long free0 = runtime.freeMemory();
                System.out.println(size + " : " + free0 + " / " + runtime.totalMemory());
                byte[] fileBytes = new byte[(int) size];
                long free1 = runtime.freeMemory();
                System.out.println(size + " : " + free1 + " / " + runtime.totalMemory());
            } catch (Exception ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    J'obtiens alors le résultat suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    178868492 : 15964944 / 16252928
    178868492 : 79111016 / 259522560
    J'ai donc 79111016 octets de disponible après création de mon tableau. Or dans le premier cas où j'augmente sa taille de seulement 1, la JVM me dit qu'elle n'a pas assez de place !

    Quelque chose m'échappe. Quelqu'un peut-il m'aiguiller sur les raisons de ce fonctionnement ?

    Merci d'avance !

  2. #2
    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
    Salut,



    Si tu es en Java 5 ou +, utilises les MXBean pour afficher l'état de la mémoire (c'est plus détaillé) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
                MemoryMXBean mx = ManagementFactory.getMemoryMXBean();
                System.out.println(mx.getHeapMemoryUsage());

    Sinon essayes de lancer ton code avec -XX:+PrintGC pour afficher les info du GC, ca pourrait peut-être aider...


    a++

  3. #3
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut
    Merci pour MemoryMXBean mx = ManagementFactory.getMemoryMXBean(); je ne connaissais pas, de même pour l'option -XX:+PrintGC...

    Cela dit, je n'ai pas l'impression que ça grand chose à ma compréhension...

    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
    package testheapsize;
     
    import java.lang.management.ManagementFactory;
    import java.lang.management.MemoryMXBean;
    import java.util.logging.Level;
    import java.util.logging.Logger;
     
    public class Main {
     
        public static void main(String... args) {
            try {
                long size = 178867868;
     
                MemoryMXBean mx = ManagementFactory.getMemoryMXBean();
                System.out.println(mx.getHeapMemoryUsage());
                byte[] fileBytes = new byte[(int) size];
                System.out.println(mx.getHeapMemoryUsage());
            } catch (Exception ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    Affiche cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    init = 16777216(16384K) used = 288120(281K) committed = 16252928(15872K) max = 259522560(253440K)
    [GC 281K->121K(15872K), 0.4281949 secs]
    [Full GC 121K->121K(15872K), -0.4138760 secs]
    [Full GC 121K->108K(179776K), 0.0099123 secs]
    init = 16777216(16384K) used = 180411544(176183K) committed = 259522560(253440K) max = 259522560(253440K)
    Si je comprends bien, j'ai donc le "commited" (qui est bien égal au max) moins le "used" de disponible après création de mon tableau de bytes, soit 259522560 - 180411544 = environ 80 000 000 octets.
    Pourtant, si j'augmente de 1 ma taille du tableau (de 178867868 à 178867869 - la valeur a changé par rapport au test de mon post initial, mais ça se comprend par la taille prise par l'objet mx) :
    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
    package testheapsize;
     
    import java.lang.management.ManagementFactory;
    import java.lang.management.MemoryMXBean;
    import java.util.logging.Level;
    import java.util.logging.Logger;
     
    public class Main {
     
        public static void main(String... args) {
            try {
                long size = 178867869;
     
                MemoryMXBean mx = ManagementFactory.getMemoryMXBean();
                System.out.println(mx.getHeapMemoryUsage());
                byte[] fileBytes = new byte[(int) size];
                System.out.println(mx.getHeapMemoryUsage());
            } catch (Exception ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    J'obtiens :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    init = 16777216(16384K) used = 288120(281K) committed = 16252928(15872K) max = 259522560(253440K)
    [GC 281K->121K(15872K), -0.4252731 secs]
    [Full GC 121K->121K(15872K), 0.4393221 secs]
    [Full GC 121K->108K(179776K), 0.0096222 secs]
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
            at testheapsize.Main.main(Main.java:16)
    Java Result: 1
    Donc toujours dans l'incompréhension...

    En tout cas, merci de t'intéresser à mon sujet

  4. #4
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut
    Je précise que je ne cherche pas à comprendre cela pour le côté "sportif" de la chose, mais parce que j'ai une application qui demande à charger un gros fichier en mémoire et qui nécessite un heap size énorme comparé à la taille du fichier à charger...

    Donc j'aimerai comprendre cela avant de penser à une autre solution...

  5. #5
    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
    Après être allé à la pèche aux infos, je suis tomber là dessus : http://stackoverflow.com/questions/7...mory-available

    En gros les tableaux doivent être alloué sur un même bloc. Le problème c'est que le heap est déjà découpé en plusieurs parties, et donc plus le tableau est grand plus il est dur de lui trouver une place. Là ton tableau correspond quand même à presque 70% de ton heap...


    Après à 1 octets près c'est pas de bol !!!
    Mais ce doit être une limite "arbitraire" (d'ailleurs perso je ne reproduit pas le problème. Enfin pas avec les mêmes valeurs).


    Citation Envoyé par Claythest Voir le message
    Je précise que je ne cherche pas à comprendre cela pour le côté "sportif" de la chose, mais parce que j'ai une application qui demande à charger un gros fichier en mémoire et qui nécessite un heap size énorme comparé à la taille du fichier à charger...
    Tu as donc plusieurs solutions :
    • Augmenter ton max-heap-size.
    • Traiter ton fichier par bloc, pour éviter de la charger entièrement en mémoire.
    • Utiliser des ByteBuffer avec allocateDirect(), qui n'utilise pas le heap.



    a++

  6. #6
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut
    Merci énormément pour cette trouvaille !

    Après à 1 octets près c'est pas de bol !!!
    J'ai fait exprès de trouver cette limite afin d'arriver à ce qui me paraissait absurde (à 1 octet près, un coup j'ai plus de mémoire, un coup j'ai 80Mo de disponible...) dans mon environnement. Je pense que cette limite dépend du compilateur, de la machine (32 bits / 64 bits) et de la JVM... Mais au fond peu importe la valeur... D'ailleurs entre les 2 bouts de codes (sans et avec le MemoryMXBean ), cette valeur a changé.

    Concernant les solutions :
    - Augmenter le heap size
    => Le problème c'est qu'aujourd'hui c'est un fichier de 200 Mo qui me pose problème, mais demain si un client me sort un fichier de 1 Go (et ça pourrait arriver), il me faudrait beaucoup plus de RAM que nécessaire, or sur une machine 32 bits le max heap size est aux environ de 1,7Go, ce qui pourrait ne pas suffire.

    - Traiter ton fichier par bloc, pour éviter de la charger entièrement en mémoire.
    => En fait c'est ce que je faisais avant, mais pour des raisons de performances, et pensant que tant qu'il y a de la place dans le tas, on peut charger le fichier intégralement en RAM, je pensais avoir trouvé la solution miracle. Donc en effet, je vais peut être devoir faire machine arrière.

    - Utiliser des ByteBuffer avec allocateDirect(), qui n'utilise pas le heap.
    Alors ça je ne connaissais pas, et je vais me pencher dessus tout de suite ! Je ne savais pas qu'il était possible de "sortir" du heap...

    Donc merci pour ton aide précieuse (comme d'habitude d'ailleurs ) !

    Et petite question subsidiaire : le garbage collector ne défragmente pas le tas si besoin je me trompe ? Car si c'est bien le cas, cela veut en effet dire qu'il est plus que déconseillé de créer de gros tableaux pendant que l'application "vie" ( = plusieurs objets ont été créés, la mémoire est donc potentiellement fortement fragmentée). Moi qui pensait (naïvement certes :p) que tant qu'il y avait de la place, ça ne posait pas de soucis...

  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 Claythest Voir le message
    Alors ça je ne connaissais pas, et je vais me pencher dessus tout de suite ! Je ne savais pas qu'il était possible de "sortir" du heap...
    En fait je viens de voir qu'il y avait aussi une limite pour ByteBuffer.allocateDirect()
    Par défaut c'est la même limite que le max-heap (-Xmx), même si cela n'a rien à voir
    On peut modifier cette valeur via -XX:MaxDirectMemorySize : http://www.gemstone.com/docs/html/ge...uning.8.4.html



    Citation Envoyé par Claythest Voir le message
    Et petite question subsidiaire : le garbage collector ne défragmente pas le tas si besoin je me trompe ?
    Je pense que le GC doit surement défragmenter le tas (je ne peux pas le confirmer toutefois).
    Mais le heap est lui-même divisé en plusieurs zones, donc rien ne garantie que la zone mémoire libre puisse être utilisé en un seul bloc...




    Pour ton problème, je pense qu'il serait fortement préférable de traiter ton fichier par bloc, surtout qu'apparemment c'est possible.

    Tu avais des problèmes de perfs ? De quels genres ? Que fais-tu de ces données ?


    a++

  8. #8
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut
    En fait tu as raison... J'avais la tête dans le guidon lorsque j'ai lu ta réponse ... trop content de découvrir une nouvelle fonctionnalité de Java (réserver de la mémoire hors du tas).

    En fait ce que je fais actuellement, c'est que je charge un fichier en mémoire dans un tableau d'octets et l'envoie en brut à mon serveur (serveur JBoss - EJB3).
    Il faut que je revois ce mécanisme pour lire et envoyer à la volée, car même en utilisant un ByteBuffer, je ne peux pas ensuite l'envoyer à mon serveur (objet non sérializable, et ça se comprend).

    Il me faut me tourner donc vers le moyen d'envoyer un fichier via des EJB. Ce sujet est donc clos, nouvelle piste de recherche, et donc peut-être nouvelles questions mais sur un tout autre sujet !

    Encore merci !

  9. #9
    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
    je connais pas à fond les EJB remote, mais avec un inputstream, ça ne lui convient pas à l'EJB? Le code ejb-client ne va pas streamer le contenu du inputstream vers le serveur?

  10. #10
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut
    Citation Envoyé par tchize_ Voir le message
    je connais pas à fond les EJB remote, mais avec un inputstream, ça ne lui convient pas à l'EJB? Le code ejb-client ne va pas streamer le contenu du inputstream vers le serveur?
    Bon ben tu me forces à continuer la discussion sur ce même sujet

    En fait via les EJB, on ne peut envoyer (et récupérer) que des objets Serializable (ça communique via le réseau, il lui faut donc pouvoir écrire et lire l'objet dans un flux). Or InputStream n'est pas sérializable.

    J'ai trouvé ce lien intéressant.
    Mais résultat des courses, ils conseillent de faire ce que je faisais avant, c'est à dire lire tout d'un coup (donc dans plusieurs petits tableaux au lieu d'un seul gros cf le début de ce topic) et tout envoyer en bloc. Pas de lecture et d'envoi à la volée...

    A moins que org.jboss.resource.adapter.jdbc.remote.SerializableInputStream fonctionne. Je vais tester sur le champ

  11. #11
    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
    Eventuellement? http://code.google.com/p/stream-serializer/

    Sinon, pour une problème similaire avec JMS (transfert de gros paquets de données via JMS, plusieurs Gigas), la solution a été la suivante:

    1) uploader le fichier sur un serveur de type REST
    2) stocker un message JMS donnant le nom du fichier sur le serveur REST
    3) le client de l'autre coté récupère le fichier sur le serveur REST, fait son petit bordel, et efface sur le serveur REST.

    Bon, l'avantage c'était que pour moi activemq gérait ça en interne comme un grand

  12. #12
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut
    En utilisant org.jboss.resource.adapter.jdbc.remote.SerializableInputStream j'obtiens........... (roulement de tambour) ..... Un magnifique OutOfMemoryError !!!
    Je pige pas pourquoi il a été codé comme ça ce truc : Source de SerializableInputStream
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    data = new byte[byteList.size()];
    Il lit le fichier dans un unique tableau d'octet comme je faisais quoi, donc forcément ça plante pareil... sauf que lui il se fait passer pour un InputStream !

    Bref, ceci ayant échoué, je teste ton code tchize_ !

  13. #13
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut
    Finalement ce n'est pas mieux, il utilise également la même technique, à savoir lecture intégrale du fichier dans un gros tableau de byte, et donc OutOfMemoryError...

    Je vais voir si je ne peux pas faire moi même le travail à savoir interconnecter les 2 Stream (le serializable avec celui côté client)...

  14. #14
    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
    prend ta classe (ayant un File), code la comme Externalizable, surcharge writeObjet / readObjet, et fais toi même la sérialisation / désérialisation.

    Pour la sérialisation, j'écrirais la taille du fichier + son nom + les données dans le flux. Pour la désérialisation, je créerais un fichier temporaire, et je lirais progressivement le flux pour écrire dans ce fichier temporaire. Ca éviterais d'encombrer la mémoire.

    Je suis assez déçu que tout mette en mémoire


    Edit: j'ai mis un commentaire sur leurs issues
    http://code.google.com/p/stream-seri...&ts=1333109241

  15. #15
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut
    Citation Envoyé par tchize_ Voir le message
    prend ta classe (ayant un File), code la comme Externalizable, surcharge writeObjet / readObjet, et fais toi même la sérialisation / désérialisation.

    Pour la sérialisation, j'écrirais la taille du fichier + son nom + les données dans le flux. Pour la désérialisation, je créerais un fichier temporaire, et je lirais progressivement le flux pour écrire dans ce fichier temporaire. Ca éviterais d'encombrer la mémoire.

    Je suis assez déçu que tout mette en mémoire


    Edit: j'ai mis un commentaire sur leurs issues
    http://code.google.com/p/stream-seri...&ts=1333109241
    Je suis bluffé par ton idée ! Je ne trouvais pas de solutions... Je teste cela de suite !

  16. #16
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut
    Alors je dois toucher à des choses que je ne maîtrise pas (les méandres de la sérialisation).

    Voici la classe que j'ai codé :
    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
    public class SerializableInputStream implements Externalizable {
     
        private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
        private static final String DEFAULT_SUFFIX_TMP_FILE = null; // => .tmp
        private InputStream inputStream;
     
        public SerializableInputStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }
     
        public InputStream getInputStream() {
            return inputStream;
        }
     
        public void writeExternal(ObjectOutput out) throws IOException {
            byte[] buff = new byte[DEFAULT_BUFFER_SIZE];
            int l = inputStream.read(buff);
            while (l > 0) {
                out.write(buff, 0, l);
                l = inputStream.read(buff);
            }
            inputStream.close();
        }
     
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            File tmp = File.createTempFile(SerializableInputStream.class.getName() + hashCode(), DEFAULT_SUFFIX_TMP_FILE);
            FileOutputStream out = new FileOutputStream(tmp);
            byte[] buff = new byte[DEFAULT_BUFFER_SIZE];
            int l = in.read(buff);
            while (l > 0) {
                out.write(buff, 0, l);
                l = in.read(buff);
            }
            out.close();
     
            inputStream = new FileInputStream(tmp);
        }
    }
    Lorsque j'ai écrit cette classe, tout me paraissait hyper clair et magique ! L'utilisation de cette classe est on ne peut plus simple : on crée son InputStream, on crée un SerializableInputStream avec pour attribut cet inputStream, on l'envoie au serveur, le serveur récupère l'inputStream via la méthode getInputStream et lit donc ce stream avant de le fermer (on a l'impression d'ouvrir le stream côté client et de le fermer côté serveur :p). Le tout transparent, donc facile à utiliser...

    Malheureusement, j'obtiens ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Exception in thread "Thread-20" java.lang.OutOfMemoryError: Java heap space
            at java.util.Arrays.copyOf(Arrays.java:2786)
            at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94)
            at java.io.ObjectOutputStream$BlockDataOutputStream.write(ObjectOutputStream.java:1823)
            at java.io.ObjectOutputStream.write(ObjectOutputStream.java:689)
            at xx.xxxxx.xxxxx.xxx.SerializableInputStream.writeExternal(SerializableInputStream.java:38)
    La ligne 38 étant : La sérialisation elle aussi essaie de tout mettre dans un tableau d'octets ?
    Ou alors ce n'est pas ce que tu voulais que je fasse et j'ai mal compris une chose ? Ou alors tu as une idée pour contrecarrer ce mécanisme ? Parce que je t'avoue que là, je n'ai pas d'idée, il me faudrait me plonger plus en détail sur la sérialisation en Java pour voir comment redéfinir celle-ci... si c'est possible...

  17. #17
    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
    Ton code me semble correct, et fonctionne bien pour sérialiser un flux de 600Mo avec une JVM limité à 64Mo max...


    Donc je suppose que c'est le mécanisme lié aux EJB qui sérialise tout en mémoire avant d'envoyer les données



    Je ne connais pas le mécanisme des EJBs, mais ce n'est peut-être pas la meilleur solution dans ce cas là.
    Tu n'as pas un serveur FTP ou HTTP à disposition pour transmettre le fichier ?


    a++

  18. #18
    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
    Citation Envoyé par Claythest Voir le message
    La sérialisation elle aussi essaie de tout mettre dans un tableau d'octets ?
    Ha oui, alors là tu l'a dans l'OS. Quel que soit le code responsable de la serialisation dans ton conteneur EJB, ce cochon sérialise tout en mémoire avant de l'envoyer, car il lie le ObjectOutputStream à un ByteArrayOutputStream On pourrais avoir le stack complet pour connaitre les classes impliquées?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94)
            at java.io.ObjectOutputStream$BlockDataOutputStream.write(ObjectOutputStream.java:1823)
    Ou alors ce n'est pas ce que tu voulais que je fasse et j'ai mal compris une chose ?
    c'est ce que je suggérait. J'aurais du rajouter "en sepérant que la sérialisation ne se fasse pas en mémoire"


    Bon ben je vois plus que deux possibilité:
    1) on garde ça et on essaie de voir si on peux pas faire changer d'avis à ton conteneur EJB (pas sérialiser, méchant)

    2) on stocke pas le contenu dans ObjectOutputStream mais sur un serveur à part (ma reflexion précédente sur un serveur REST, mais c'est plus chaud à maintenir)

  19. #19
    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
    Question : dans quel sens doit se faire l'envoi ? client -> serveur ou l'inverse ?


    Parce qu'une petite servlet pourrait facilement faire l'affaire... Non ?


    a++

  20. #20
    Membre confirmé Avatar de Claythest
    Profil pro
    Inscrit en
    Mai 2003
    Messages
    558
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 558
    Points : 554
    Points
    554
    Par défaut
    La stacktrace complète :
    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
    Exception in thread "Thread-20" java.lang.OutOfMemoryError: Java heap space
            at java.util.Arrays.copyOf(Arrays.java:2786)
            at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94)
            at java.io.ObjectOutputStream$BlockDataOutputStream.write(ObjectOutputStream.java:1823)
            at java.io.ObjectOutputStream.write(ObjectOutputStream.java:689)
            at xx.xxxx.xxx.xxxx.SerializableInputStream.writeExternal(SerializableInputStream.java:38)
            at java.io.ObjectOutputStream.writeExternalData(ObjectOutputStream.java:1429)
            at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1398)
            at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
            at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1346)
            at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1154)
            at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
            at java.rmi.MarshalledObject.<init>(MarshalledObject.java:101)
            at org.jboss.aop.joinpoint.MethodInvocation.writeExternal(MethodInvocation.java:318)
            at java.io.ObjectOutputStream.writeExternalData(ObjectOutputStream.java:1429)
            at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1398)
            at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
            at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
            at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
            at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
            at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
            at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
            at org.jboss.remoting.serialization.impl.java.JavaSerializationManager.sendObjectVersion2_2(JavaSerializationManager.java:120)
            at org.jboss.remoting.serialization.impl.java.JavaSerializationManager.sendObject(JavaSerializationManager.java:95)
            at org.jboss.remoting.marshal.serializable.SerializableMarshaller.write(SerializableMarshaller.java:120)
            at org.jboss.remoting.transport.socket.MicroSocketClientInvoker.versionedWrite(MicroSocketClientInvoker.java:981)
            at org.jboss.remoting.transport.socket.MicroSocketClientInvoker.transport(MicroSocketClientInvoker.java:559)
            at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:122)
            at org.jboss.remoting.Client.invoke(Client.java:1634)
            at org.jboss.remoting.Client.invoke(Client.java:548)
            at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:62)
            at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
            at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:67)
    Je sais que JBoss est très configurable, et en fouillant bien, on doit pouvoir lui changer de "JavaSerializationManager". Je vais regarder de ce côté là, mais j'ai peur que ce soit hyper compliqué à implémenter...

    L'envoi c'est dans les 2 sens : client -> serveur et serveur -> client. J'aimerai bien rester avec la même architecture pour des raisons de maintenabilité. Je pense que si je n'y arrive pas via cette serialisation, je vais revenir avec plusieurs tableaux de bytes et alors tant pis, les fichiers volumineux ne pourront être utilisés... Si le client gueule trop, alors j'y réfléchirai à nouveau Peut-être en effet qu'une servlet ferait l'affaire dans ce cas là...

    Pour conclure ce post donc : je vais regarder du côté de JBoss si je peux changer quelque chose à sa manière de sérialiser...

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Gestion de la mémoire en Java
    Par scratch_1 dans le forum Général Java
    Réponses: 9
    Dernier message: 06/10/2009, 16h15
  2. Réponses: 13
    Dernier message: 14/02/2008, 14h27
  3. Gestion des variables - mémoire ?
    Par RIVOLLET dans le forum Langage
    Réponses: 4
    Dernier message: 26/10/2002, 13h44

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