Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 15
  1. #1
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308

    [C#/Xna] Questa sconosciuta Garbage

    Ho realizzato in Xna un programma fantascientifico che permette la navigazione in un sistema planetario. Gli oggetti sono costruiti tramite moltissime strutture, alcune molto leggere che descrivono un pianeta "in lontananza" (modelli "Far", le cui istanze sono semplici sfere nude), ed altre molto pesanti per i grossi pianeti da sorvolare o da esplorare al suolo (modelli "Near", con milioni di primitive modellati da heightmaps e ricoperti da textures). E' impossibile postare il codice ma il riassunto del Main è qui:

    codice:
    //Namespaces
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using System;
    using System.Collections;
    using System.Windows.Forms;
    using System.Collections.Generic;
    namespace Progetto_Albireo   
    {
        public class My : Microsoft.Xna.Framework.Game
        {
            //Generals          
            public static GraphicsDeviceManager Graphicsdevicemanager;
            .............
            public static string StarType = "Progetto_Albireo.Star"; MODELLO STELLA 'FAR' (leggero)
            public static string TStarType = "Progetto_Albireo.TStar"; MODELLO STELLA 'NEAR' (leggero)
            //Worlds
            public static string SphType = "Progetto_Albireo.SphWorld"; MODELLO PIANETA 'FAR' (leggero)
            public static string SphHType = "Progetto_Albireo.SphHWorld";  MODELLO PIANETA 'NEAR' (molto pesante)
            ..... ecc.
            //FUNZIONI DI UTILITY
            public static float VelocityAt(float G, float M, float d)
            {
                return (float)Math.Sqrt(G * M / d);
            }
            .......... ecc.
            //METODO CHE INIZIALIZZA IL SISTEMA
            public void InitSystem()
            {
                Planets.Add("A", new Star(...);    
    
                Planets.Add("B", new SphWorld(....);  
                ................ ecc.
     
            }
     
            public My()
            {
                //costruiamo un nostro GraphicsDeviceManager
                //la sua proprietà più importante è il GraphicsDevice
                Graphicsdevicemanager = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
            }
    
            protected override void Initialize()
            {
                Spritebatch = new SpriteBatch(GraphicsDevice);
                ........... ecc.
                base.Initialize();
            }
            protected override void LoadContent()
            {
                Background = Content.Load<Texture2D>("back");
                
                ............. ecc.
                InitSystem();
            }
            protected override void Update(GameTime gametime)
            {
                My.Discovery.Depthlist.Clear(); LISTA DEGLI OGGETTI 
                                                IN ORDINE DI DISTANZA DALLA CAMERA (astronave)
                TStar.H1("A"); METODO STATICO CHE TRASFORMA "A" DA 'Sta'r IN 'TStar' 
                               SE CI SI AVVICINA TROPPO E LO RIPORTA A 'Star' SE CI SI ALLONTANA; 
                               AGGIORNA LA Dephtlist                     
                SphHWorld.H1("B", 0f, Vector3.Zero); METODO STATICO CHE TRASFORMA "B" DA 'SphWorld' IN 'SphHWorld'
                                                      SE CI SI AVVICINA TROPPO E LO RIPORTA A 'SphWorld' SE CI SI ALLONTANA;
                                                      AGGIORNA LA Dephtlist  
                .......... ecc.
            }
    
            protected override void Draw(GameTime gametime)
            {
                GraphicsDevice.Clear(Color.Black);
                 
      
                string T;
                foreach (KeyValuePair<double, string> item in Discovery.Depthlist)
                {
                    double d = item.Key;
                    string k = item.Value;
                    T = My.Planets[k].GetType().ToString();
                    if (T == My.StarType)
                    {
                        Star x = (Star)Planets[k];
                        x.Draw(GraphicsDevice);
                    }
                    else if (T == My.TStarType)
                    {
                        TStar x = (TStar)Planets[k];
                        x.Draw(GraphicsDevice);
                    }
                    else if (T == My.SphType)
                    {
                        SphWorld x = (SphWorld)Planets[k];
                        x.Draw(GraphicsDevice);
                    }
                    else if (T == My.SphHType)
                    {
                        SphHWorld x = (SphHWorld)Planets[k];
                        x.Draw(GraphicsDevice);
                    }
                    else if ........... ecc.
                }
     
            }
    
        }
    }
    

    Il cuore del programma è il metodo statico H1 che opera la trasformazione da "Far" a "Near" e viceversa. Questo è il riassunto:


    codice:
     
            public static void H1(string k, float FM, Vector3 L)
            {
                if (My.Planets[k].GetType().ToString() == My.SphType)
                {
                    SphWorld x = (SphWorld)My.Planets[k];
                    Vector3 d = My.Discovery.Position - x.Location;
                    //costruisce la collection di profondità
                    double D = Math.Sqrt((Math.Pow(d.X, 2) + Math.Pow(d.Y, 2) + Math.Pow(d.Z, 2)));
                    My.Discovery.Depthlist.Add(-D, k);
    
                    ...
                    //Solving
                    if (d.Length() < x.Ehorizon)
                        SphHWorld.Solve(k); 'TRASFORMA x NEL TIPO 'SphHWorld'
                }
                else
                {
                    SphHWorld x = (SphHWorld)My.Planets[k];
                    Vector3 d = My.Discovery.Position - x.Location;
                    //costruisce la collection di profondità
                    double D = Math.Sqrt((Math.Pow(d.X, 2) + Math.Pow(d.Y, 2) + Math.Pow(d.Z, 2)));
                    My.Discovery.Depthlist.Add(-D, k);
                    ...
                    //Unsolving
                    if (d.Length() > x.Ehorizon)
                        SphHWorld.Unsolve(k);'RIPORTA x IN AL TIPO 'SphWorld'
                }
            }
    

    Ecco in dettaglio i due metodi "decisivi" richiamati da H1:


    codice:
            public static void Solve(string k)
            {
                .........
    
                My.Planets.Remove(k);
     
                My.Planets.Add(k, new SphHWorld(...);
     
            }
            public static void Unsolve(string k)
            {
                ..........
    
                My.Planets.Remove(k);
    
                My.Planets.Add(k, new SphWorld(...);
            }
    

    Per avere la piena padronanza di questo programma mi manca solo di capire bene cosa succede QUI e QUI, ma soprattutto QUI:

    codice:
            public static void Solve(string k)
            {
    
                My.Planets.Remove(k);<<<<<------------------- QUI
     
                My.Planets.Add(k, new SphHWorld(...);
     
            }
            public static void Unsolve(string k)
            {
    
                My.Planets.Remove(k);<<<<<-------------------- QUI
     
                My.Planets.Add(k, new SphWorld(...);
            }
    
    Non capisco un tubo di Garbage Collection. Vorrei capire se l'oggetto k in hastable viene rimosso anche dalla memoria oltre che dall'hash, e se navigando negli spazi aperti lontano dal sistema non ci sono in memoria oggetti "Near" residenti. Un oggetto SphHWorld è veramente ingombrante....
    Spero di essere stato chiaro. Grazie
    Ultima modifica di escocat; 02-11-2013 a 16:11

  2. #2
    Nel momento in cui non ci sono più riferimenti ad un certo oggetto raggiungibili dal tuo programma, un oggetto diventa passibile di garbage collection; questo significa che, quando il runtime decide che si sta consumando troppa memoria, provvederà per i fatti suoi a rimuoverlo. Esistono metodi per forzare una garbage collection, ma normalmente non è necessario impiegarli (anzi, spesso è controproducente).
    Amaro C++, il gusto pieno dell'undefined behavior.

  3. #3
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Quote Originariamente inviata da MItaly Visualizza il messaggio
    Nel momento in cui non ci sono più riferimenti ad un certo oggetto raggiungibili dal tuo programma
    Ecco è questo che non mi entra in testa. Intanto il riferimento si intende alla struttura o all'oggetto istanziato mediante quella struttura? Cos'è per la precisione un "riferimento a SphHWorld"? Anche quando l'oggetto k di tipo SphHWorld viene eliminato, tutto il programma è costellato di riferimenti a questo oggetto e a questa struttura. Non mi è chiaro....si parla di riferimenti in esecuzione del programma o riferimenti possibili che sono presenti nel programma ma non eseguiti?
    Ultima modifica di escocat; 02-11-2013 a 19:40

  4. #4
    Quote Originariamente inviata da escocat Visualizza il messaggio
    Ecco è questo che non mi entra in testa. Intanto il riferimento si intende alla struttura o all'oggetto istanziato mediante quella struttura?
    Ovviamente all'oggetto; se un oggetto non è più raggiungibile allora si può eliminare, indipendentemente dal fatto che esistano altre variabili di quel tipo.
    Cos'è per la precisione un "riferimento a SphHWorld"?
    Qualunque variabile di un reference type (=qualunque variabile di tipo classe; sono esclusi i tipi primitivi e le struct, che invece sono dei "value type"). Se hai un minimo di dimestichezza con C o C++, tutte le variabili di tipi "by reference" sono in realtà dei puntatori; se nessun puntatore punta più ad una certa istanza, questa può essere eliminata (quando gli gira al garbage collector).
    Anche quando l'oggetto k di tipo SphHWorld viene eliminato, tutto il programma è costellato di riferimenti a questo oggetto e a questa struttura.
    Se ci sono altri riferimenti che puntano al medesimo oggetto, allora ovviamente non viene eliminato (dato che è ancora utilizzato altrove); se invece ci sono altre variabili di tipo SphHWorld che puntano ad altre istanze non ha alcuna rilevanza.
    Non mi è chiaro....si parla di riferimenti in esecuzione del programma o riferimenti possibili che sono presenti nel programma ma non eseguiti?
    Di riferimenti esistenti, cosa importa ai fini della cancellazione di una certa istanza che ci possono essere dei riferimenti ad altre istanze? Il garbage collector concettualmente fa una cosa molto semplice - se una certa istanza non è più utilizzata (=non c'è più nessuna variabile che punta ad essa) la elimina.

    In ogni caso, dovresti ripassarti un po' il tuo libro di C#, value type/reference type, GC e compagnia sono abbastanza i fondamentali del linguaggio...
    Amaro C++, il gusto pieno dell'undefined behavior.

  5. #5
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    MItaly intanto ti ringrazio per le tue risposte e mi scuso per la sciocchezza che ho detto: un nome di struttura non è un riferimento! Semmai lo è il nome dell'oggetto istanziato con quella struttura. Dunque il riferimento incriminato è la componente value dell'hashtable e tutto comincia esattamente qui:

    codice:
         //METODO CHE INIZIALIZZA IL SISTEMA
            public void InitSystem()
            {
                Planets.Add("A", new Star(...);    
    
                Planets.Add("B", new SphWorld(....);  
    
                ................ ecc.
     
          }
    
    
    Il riferimento è My.Planets[k]!
    Vediamo se ho capito: se non mi avvicino ad alcun pianeta la memoria è pervasa di oggetti SphWorld che non pesano molto. Supponiamo che lo spazio di memoria riservato all'oggetto k di hashtable sia 1000 K.
    Quando entro nell'Event-Horizon di un oggetto il suo riferimento non cambia, ma cambia il tipo dell'oggetto. Infatti nel Solve l'elemento k di hash viene prima rimosso e poi aggiunto con lo stesso indice k ma di tipo SphHWorld, ben più "pesante"

    codice:
    public static void Solve(string k)
            {
                My.Planets.Remove(k);
     
                My.Planets.Add(k, new SphHWorld(...);
     
           }
    
    Quindi a questo punto la memoria indirizzata dal "puntatore" My.Planets[k] viene "allargata" e riempita in abbondanza. Supponiamo che ora lo spazio di memoria riservato all'oggetto k sia 100000000 K.
    Questa situazione rimane finchè sorvolo o atterro su questo oggetto (pianeta). Quando lascio il pianeta, succede questo:

    codice:
    public static void Unsolve(string k)
            {
              My.Planets.Remove(k);
    
              My.Planets.Add(k, new SphWorld(...);
    
            }
    
    Il riferimento continua ad esistere perchè l'"indirizzo" My.Planets[k] non è stato modificato, è cambiato di nuovo solo il tipo della sua componente 'Value', e l'istanza è tornata ad essere "leggera". Dunque la memoria puntata da k esiste sempre, dall'inizio alla fine del programma.
    Mi chiedo: a questo punto lo spazio indirizzato da k è rimasto 100000000 K di cui solo 1000 K riempiti oppure 99999000 sono stati svuotati e resi disponibili per altre risorse? Grazie.
    Ultima modifica di escocat; 03-11-2013 a 10:24

  6. #6
    Utente di HTML.it L'avatar di rsdpzed
    Registrato dal
    Aug 2001
    Messaggi
    764
    la tua situazione è quella in cui hai due riferimenti ad un oggetto e ne disponi l'eliminazione attraverso uno dei due. L'oggetto non puo essere eliminato e non per via della GC ma perchè è ancora perfettamente lecito usarlo attraverso l'altro riferimento.

    Riducendo all'osso tu hai una cosa del genere:

    codice:
    class My {
      collection Planets;
    
      void H1(string key) {
        planet o = Planets[key];
        Solve(key);
        //o esiste o è stato eliminato? 
      }
    
      void Solve(string key) {
        Planets.Remove(key);
      }
    }
    Dopo il Solve puoi ancora utilizzare il pianeta attraverso la o. Non è stato eliminato e non lo sarà finche il riferimento o non perderà lo scope.

    Ecco cosa accade (mooolto teoricamente):
    1) Crei la collection di pianeti a livello di classe e fai partire il main (o il ciclo Update Draw nel tuo caso). Nello stack viene memorizzato un riferimento alla collection (Planets) e in memoria vengono istanziati, diciamo, 3 pianeti.
    2) Entri in H1 e nello stack memorizzi un ulteriore riferimento ad uno dei tre pianeti, diciamo il secondo. In questo momento il secondo pianeta è raggiungibile sia da questo riferimento sia dal riferimento all'intera collection.
    2a) Il Garbage collector ha bisogno di memoria e inizia ad analizzare lo stack. Trova Planets che punta ai tre pianeti in memoria e contrassegna quei tre pianeti come oggetti da non eliminare, poi trova o che punta al secondo pianeta e lo contrassegna come da non eliminare (inutile visto che l'ha fatto prima). Il GC ora comincia a scorrere la memoria per eliminare tutti gli oggetti non marcati ma non elimina nulla.
    3) Entri in Solve e viene comandata l'eliminazione del secondo pianeta. Planets ora punta ad una collection fatta di due pianeti il primo e il terzo mentre o punta al secondo pianeta.
    3a) Il GC necessita di memoria e ricomincia ad analizzare lo stack. Planets punta al primo e al terzo e i due pianeti vengono contrassegnati come oggetti da non eliminare. o punta al secondo e anche lui viene graziato. Ancora una volta scorrendo la memoria il GC non trova nulla da eliminare.
    4) Termina lo scope di Solve e si ritorna in H1. I pianeti sono intatti solo che attraverso la collection non puoi piu vedere il secondo pianeta che puoi comunque raggiungere attraverso il riferimento o.
    5) Termina H1 e quindi termina anche lo scope di 'o' il quale viene eliminato dallo stack.
    5a) Il GC ha bisogno di memoria e comincia ad analizzare lo stack. Trova Planets che punta al primo e al terzo e marca i due oggetti per non eliminarli. Non trova nientaltro nello stack. Ora va in memoria e comincia ad eliminare tutti gli oggetti che non sono stati marcati, trova il secondo pianeta e lo elimina.

    Questo fatto di tenere in vita un riferimento ad un oggetto che andrebbe eliminato, lungo una catena di chiamate è da evitare il piu possibile. Prima che per le performance la ragione principale è la consistenza dello stato del programma: Cosa ci fa in giro un pianeta nello stato di Near se il programma lo ha messo in stato di Far? Magari nel tuo caso non succede nulla ma in linea generale è (o sarà) un problema.
    Dal lato performance la cosa sicuramente crea delle ripercussioni. Se dopo il solve o l'unsolve hai bisogno di memoria, perche magari devi mettere in Near altri tot pianeti, non potrai contare su quella che si sarebbe liberata dai pianeti near contrassegnati come Far fino alla fine di H1 e cioè al prossimo giro.

    Quello che segue potrebbe stravolgere quanto detto fin'ora e per questo lo metto solo alla fine...
    E se il tipo shipworld è una struct e non un oggetto? (cosa che in xna accade poco è spesso).
    Qui le cose cambiano.
    Le struct non sono oggetti in memoria e non hanno riferimenti. le struct vengono copiate nello stack cosi come sono e vengono eliminate al termine dello scope.
    Le collection sono oggetti in memoria per cui Planets rimane cosi com'è (un array di pianeti in memoria anche se i pianeti sono struct), quello che cambia è la variabile o. o non è piu un riferimento al secondo pianeta memoria. o è una variabile nello stack dentro cui viene copiato tutto il contenuto del secondo pianeta.
    Nel punto 3a della timeline precedente il secondo pianeta viene eliminato perche 'o' non lo referenzia e quindi il GC non lo ha marcato per la 'sopravvivenza'.
    Questo risolverebbe il problema delle performance ma non risolve il primo e piu importante problema: che ci fa un o ancora in giro?

    Tutta questa pappina per arrivare alla conclusione:
    Evita questa situazione il piu possibile. quando devi modificare una collection usa la collection e non riferimenti agli oggetti dentro la collection. I riferimenti agli oggetti della collection andrebbero usati solo quando la collection non viene modificata magari quando un oggetto viene passato ad una funzione che non vede la collection intera (e quindi non puo modificaarla). In tutti gli altri casi usa direttamente il riferimento alla collection (planets[key]).

  7. #7
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ecco cosa accade (mooolto teoricamente):
    1) Crei la collection di pianeti a livello di classe e fai partire il main (o il ciclo Update Draw nel tuo caso). Nello stack viene memorizzato un riferimento alla collection (Planets) e in memoria vengono istanziati, diciamo, 3 pianeti.
    E qui ci siamo.

    2) Entri in H1 e nello stack memorizzi un ulteriore riferimento ad uno dei tre pianeti, diciamo il secondo. In questo momento il secondo pianeta è raggiungibile sia da questo riferimento sia dal riferimento all'intera collection.
    OK. il secondo pianeta è l'oggetto di tipo SphWorld (leggero), raggiungibile con Planets["B"] e 'x'. Altri riferimenti: Planets["A"] e Planets["C"].

    2a) Il Garbage collector ha bisogno di memoria e inizia ad analizzare lo stack. Trova Planets che punta ai tre pianeti in memoria e contrassegna quei tre pianeti come oggetti da non eliminare, poi trova o che punta al secondo pianeta e lo contrassegna come da non eliminare (inutile visto che l'ha fatto prima). Il GC ora comincia a scorrere la memoria per eliminare tutti gli oggetti non marcati ma non elimina nulla.
    3) Entri in Solve e viene comandata l'eliminazione del secondo pianeta. Planets ora punta ad una collection fatta di due pianeti il primo e il terzo mentre o punta al secondo pianeta.
    OK. Sono sul pianeta "B".

    codice:
     
          public static void H1(string k, float FM, Vector3 L)
            {
                 if (My.Planets[k].GetType().ToString() == My.SphType)
    
               {
                    SphWorld x = (SphWorld)My.Planets[k];              Vector3 d = My.Discovery.Position - x.Location;
                    //costruisce la collection di profondità
                    double D = Math.Sqrt((Math.Pow(d.X, 2) + Math.Pow(d.Y, 2) + Math.Pow(d.Z, 2)));
                  My.Discovery.Depthlist.Add(-D, k);
    
                    ...
                  //Solving
                    if (d.Length() < x.Ehorizon)
                       SphHWorld.Solve(k); 'TRASFORMA x NEL TIPO 'SphHWorld'
                }
    
                else
    
                {
                    SphHWorld x = (SphHWorld)My.Planets[k];
                  Vector3 d = My.Discovery.Position - x.Location;
                    //costruisce la collection di profondità
                    double D = Math.Sqrt((Math.Pow(d.X, 2) + Math.Pow(d.Y, 2) + Math.Pow(d.Z, 2)));
                  My.Discovery.Depthlist.Add(-D, k);
                  ...
                  //Unsolving
                    if (d.Length() > x.Ehorizon)
                        SphHWorld.Unsolve(k);'RIPORTA x IN AL TIPO 'SphWorld'
                }
    
            }
    codice:
          public static void Solve(string k)
            {
    
                My.Planets.Remove(k);  
    
                My.Planets.Add(k, new SphHWorld(...);
    
          }
    RIFERIMENTI ATTIVI:
    Planets["A"] (Far = leggero).
    Planets["B"] (Near = pesante).
    Planets["C"] (Far)
    x (Far)

    3a) Il GC necessita di memoria e ricomincia ad analizzare lo stack. Planets punta al primo e al terzo e i due pianeti vengono contrassegnati come oggetti da non eliminare. o punta al secondo e anche lui viene graziato. Ancora una volta scorrendo la memoria il GC non trova nulla da eliminare.
    OK. Ma Planets[] punta anche a "B".

    4) Termina lo scope di Solve e si ritorna in H1. I pianeti sono intatti solo che attraverso la collection non puoi piu vedere il secondo pianeta che puoi comunque raggiungere attraverso il riferimento o.
    NO. La collection ha sempre tre pianeti. O sbaglio?

    5) Termina H1 e quindi termina anche lo scope di 'o' il quale viene eliminato dallo stack.
    OK. RIFERIMENTI ATTIVI:
    Planets["A"](Far)
    Planets["B"](Near)
    Planets["C"](Far)

    5a) Il GC ha bisogno di memoria e comincia ad analizzare lo stack. Trova Planets che punta al primo e al terzo e marca i due oggetti per non eliminarli. Non trova nientaltro nello stack. Ora va in memoria e comincia ad eliminare tutti gli oggetti che non sono stati marcati, trova il secondo pianeta e lo elimina.
    NO. "B" ESISTE. IO CI SONO SOPRA!.

    Questo fatto di tenere in vita un riferimento ad un oggetto che andrebbe eliminato, lungo una catena di chiamate è da evitare il piu possibile. Prima che per le performance la ragione principale è la consistenza dello stato del programma: Cosa ci fa in giro un pianeta nello stato di Near se il programma lo ha messo in stato di Far? Magari nel tuo caso non succede nulla ma in linea generale è (o sarà) un problema.
    Dal lato performance la cosa sicuramente crea delle ripercussioni. Se dopo il solve o l'unsolve hai bisogno di memoria, perche magari devi mettere in Near altri tot pianeti, non potrai contare su quella che si sarebbe liberata dai pianeti near contrassegnati come Far fino alla fine di H1 e cioè al prossimo giro.
    ??????????????????
    Forse non ho ben compreso ....
    Ultima modifica di escocat; 03-11-2013 a 12:14

  8. #8
    Utente di HTML.it L'avatar di rsdpzed
    Registrato dal
    Aug 2001
    Messaggi
    764
    ho usato un esempio ridotto al necessario per rispondere ai dubbi su come, se e quando veniva resa disponibile la memoria nel tuo programma. I passaggi devono riferirsi al mio codice (che non prevede il reinserimento del pianeta sotto altro tipo, cosa che avrebbe introdotto un elemento di complessità nella spiegazione). Capito quel codice e quel procedimento avresti trovato la risposta ai tuoi dubbi.

    Non rifarò tutti i passaggi da capo, ti correggo solo i punti in cui sbagli:

    RIFERIMENTI ATTIVI:
    Planets["A"] (Far = leggero).
    Planets["B"] (Near = pesante).
    Planets["C"] (Far)
    x (Far)
    quel Planets["B"] è un QUARTO pianeta. il fatto che abbia B come chiave non significa nulladal punto di vista della memoria, in memoria ci sono 4 pianeti distinti in quello stage. (il secondo puntato da x e che finche c'è x non puo essere eliminato dalla memoria).

    NO. "B" ESISTE. IO CI SONO SOPRA!.
    ecco appunto io ho detto secondo non B. B esiste ed è il quarto pianeta creato DOPO aver tolto il secondo dai riferimenti della collection. La GC (come ho detto) elimina il secondo, quello puntato da x e dopo che x è uscita dallo scope.
    Ora e solo dopo l'eliminazione del secondo pianeta sei a tre pianeti in memoria:
    A il primo
    B il quarto
    C il terzo.


    ??????????????????
    mi riferisco al fatto che c'è un momento in cui ci sono 4 pianeti accessibili e presenti in memoria e questo non va bene. Per risolvere basta evitare di usare variabili come la x e usare Planets[key] direttamente.

  9. #9
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    RIFERIMENTI ATTIVI:
    Planets["A"] (Far = leggero).
    Planets["B"] (Near = pesante).
    Planets["C"] (Far)
    x (Far)



    quel Planets["B"] è un QUARTO pianeta. il fatto che abbia B come chiave non significa nulladal punto di vista della memoria, in memoria ci sono 4 pianeti distinti in quello stage. (il secondo puntato da x e che finche c'è x non puo essere eliminato dalla memoria).
    mmmmmm....4° pianeta?
    Ho bisogno di chiarirmi le idee, e ti farei una statua per la tua santa pazienza.

    1) Ma se un hash ha 3 elementi e ne rimuovo uno, gli elementi dell'hash non si riducono a 2? L'hashtable non è una lista? Certe volte usiamo bene le cose ma non le capiamo a fondo....

    2) Se dall'hashtable rimuovo l'elemento k-esimo che aveva key "B" e aggiungo un elemento con key "B" più "pesante", cosa succede in memoria? Viene semplicemente "espansa" la stessa memoria indirizzata o si creano due aree di memoria diverse?

    Grazie.

  10. #10
    Ragazzi, mi sembra che stiate facendo su un "rebelòtt"

    La questione è molto più semplice:
    • se A è un reference type (ovvero, è una classe, non una struct o un tipo primitivo), tutte le volte che dichiari una variabile di tipo A in realtà stai dichiarando un riferimento ad A; puoi pensare ad un riferimento come ad un "filo" che collega il nome della tua variabile all'oggetto (=istanza) vero e proprio;
    • le istanze degli oggetti stanno nell'heap gestito; di base gli oggetti vengono costruiti (e la memoria viene allocata) solo quando c'è una "new"; se tu scrivi
      codice:
      A myA = new A();
      stai facendo tre cose distinte e molto diverse:
      • A myA dichiara un reference locale ad A, allocato sullo stack;
      • new A() crea nell'heap un'istanza di A
      • = fa puntare il riferimento myA all'oggetto appena creato sull'heap.

    In generale, le variabili-riferimento e gli oggetti veri e propri allocati sull'heap non sono accoppiati in maniera stretta; se un riferimento esce di scope, oppure se crei un altro riferimento (=dichiari un'altra variabile) che punta allo stesso oggetto (A anotherA=myA;), all'oggetto vero e proprio non importa niente - lui sta lì per i fatti suoi sull'heap, dei riferimenti che puntano a lui non gliene frega niente.

    Se tutta la questione terminasse così, ci sarebbe un serio problema: dato che l'uscita di scope dei reference non ha alcun effetto sugli oggetti sull'heap, dopo un po' l'heap si riempirebbe di spazzatura, ovvero di oggetti che non servono più, non essendo più raggiungibili tramite reference da parte del programma.
    Qui entra in scena il garbage collector; quando il GC ne ha voglia, provvede ad esaminare tutto l'albero di riferimenti, segnandosi quali oggetti sono ancora raggiungibili da parte del programma (e quindi sono in uso); alla fine del procedimento, provvede ad eliminare quelli che non sono stati marcati, dato che sono tutta roba che non serve più (dato che il programma non vi può accedere tramite riferimenti, significa che è tutta roba non più usata).

    In un certo senso, puoi vedere gli oggetti come dei palloncini e i riferimenti come dei fili; quando crei un nuovo oggetto, gonfi un palloncino, e assegnando il risultato della new ad una variabile ci leghi il suo filo; quando lo memorizzi in una collection, lo passi a funzioni, ... non stai facendo altro che legarci dei nuovi fili che passi ad altri pezzi del tuo codice. Ogni volta che un reference esce di scope (o ci viene assegnato null, oppure viene fatto puntare ad un altro oggetto), viene tagliato il suo filo (eventualmente ri-legandolo ad un altro oggetto se stai riassegnando il reference), e quando non c'è più nessun filo il palloncino vola via. Il GC non è altro che la "contraerea" , che, per evitare che il cielo si riempia di palloncini vecchi, di tanto in tanto spara pallini e abbatte i palloncini senza fili.

    Tornando quindi al tuo codice, anche l'hashtable in realtà è una hashtable di riferimenti; se rimuovi un elemento dalla hashtable, stai uccidendo un reference, e l'oggetto (palloncino) a cui puntava, se non ha altri riferimenti (fili), diventa passibile di garbage collection (contraerea ). Quando tu rimpiazzi un elemento nella hashtable, quello che fai è:
    • tagliare il filo al vecchio palloncino; se non ci sono altri palloncini, questo vola via, e tra un po' potrebbe essere abbattuto;
    • gonfiare un nuovo palloncino (nel tuo caso più grosso);
    • legare il nuovo palloncino allo stesso gancio a cui era attaccato il vecchio.

    (in realtà anche la hashtable è un oggetto a cui c'è un riferimento come membro della classe, per cui in realtà sono palloncini legati ad altri palloncini che sono ancorati a terra mediante variabili sullo stack o variabili globali, ma la metafora inizia ad incasinarsi un po' troppo )

    In tutto questo, come puoi vedere non viene espansa "memoria vecchia", ma piuttosto vengono rilasciati vecchi oggetti e allocati di nuovi. La chiave per evitare di sprecare memoria ovviamente è non mantenere riferimenti "di lunga durata" ad oggetti che non servono più - una volta rimosso un oggetto dalla hashtable, ad esempio, se hai altre variabili che puntano al "vecchio" oggetto può convenire impostarle a null (anche se va detto che la situazione non è così tragica come la dipingeva rsdpzed, di norma dei riferimenti locali ad una funzione è difficile che diano problemi, visto che spariscono appena escono di scope; quello che importa davvero sono riferimenti a livello di classe o memorizzati in altri oggetti).

    ---

    Per inciso, dando un'occhiata al tuo codice mi sembra ci sia una certa confusione su static e compagnia - nello specifico, inizializzi variabili static dentro al costruttore d'istanza in linea di massima nella tua classe non credo ci dovrebbe essere alcunché di static.
    Ultima modifica di MItaly; 03-11-2013 a 15:55
    Amaro C++, il gusto pieno dell'undefined behavior.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.