Sto lavorando con una grande quantità di oggetti (POI) che vengono visualizzati su un MapControl
. Mi sto aiutando con un MVVM Light ad obbedire alle regole dell'approccio MVVM.Perdita di memoria Windows Phone 8.1 WinRT con ObservableCollection
Poiché sono obbligato a visualizzare tutti gli oggetti sulla mappa, devo utilizzare la raccolta MapItemsControl
e non quella di MapElements
. Questa raccolta si lega all'oggetto ObservableCollection<PushpinViewModel>
(Pushpins
) nel corrispondente ViewModel
. Tutto funziona come previsto, fino al punto, quando voglio aggiornare Pushpins
. Il problema è la perdita di memoria. Ma prima, un po 'di codice per visualizzare il problema:
XAML:
<maps:MapControl x:Name="Map"
x:Uid="MapControl">
<maps:MapItemsControl ItemsSource="{Binding Pushpins}">
<maps:MapItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Image}"/>
</DataTemplate>
</maps:MapItemsControl.ItemTemplate>
</maps:MapItemsControl>
MainViewModel:
public class MainViewModel : ViewModelBase
{
public RelayCommand AddCommand { get; set; }
public RelayCommand ClearCommand { get; set; }
public RelayCommand CollectCommand { get; set; }
public ObservableCollection<PushpinViewModel> Pushpins { get; set; }
/* Ctor, initialization of Pushpins and stuff like that */
private void Collect()
{
GC.Collect(2);
GC.WaitForPendingFinalizers();
GC.Collect(2);
PrintCurrentMemory();
}
private void Clear()
{
Pushpins.Clear();
PrintCurrentMemory();
}
private void Add()
{
for (int i = 0; i < 1000; i++)
{
Pushpins.Add(new PushpinViewModel());
}
PrintCurrentMemory();
}
private void PrintCurrentMemory()
{
Logger.Log(String.Format("Total Memory: {0}", GC.GetTotalMemory(true)/1024.0));
}
}
PushpinViewModel:
public class PushpinViewModel: ViewModelBase
{
public string Image { get { return "/Assets/SomeImage.png"; } }
~PushpinViewModel()
{
Logger.Log("This finalizer never gets called!");
}
}
Ora, si consideri il seguente scenario. Aggiungo alla collezione Pushpins
elementi 1000 PushpinViewModel
. Sono resi, la memoria è allocata, va tutto bene. Ora voglio cancellare la raccolta e aggiungere un altro (diverso nello scenario reale) 1000 elementi. Quindi, chiamo il metodo Clear()
. Ma .. non succede nulla! Pushpins
viene cancellato, ma i finalizzatori di PushpinViewModel
non vengono richiamati! Quindi aggiungo di nuovo 1000 elementi e l'utilizzo della memoria raddoppia. Puoi indovinare cosa succederà dopo. Quando ripeto questa procedura Clear()
- Add()
per 3-5 volte la mia app si arresta in modo anomalo.
Quindi, qual è il problema? Chiaramente, ObservableCollection
contiene i riferimenti agli oggetti PushpinViewModel
dopo che è stato eseguito su Clear()
, quindi non possono essere raccolti. Ovviamente forzare GC a eseguire la garbage collection non aiuta (a volte peggiora persino la situazione).
Mi sta dando fastidio per 2 giorni, ho provato molti scenari diversi per cercare di superare questo problema, ma ad essere sincero, nulla ha aiutato. C'era solo una cosa che non valeva niente - non ricordo lo scenario esatto, ma quando ho assegnato lo Pushpins = null
e poi ho fatto qualcosa di più, gli VehiceViewModel
sono stati distrutti. Ma questo non funziona per me, perché ricordo anche che ho avuto problemi con la visualizzazione di questi pin sulla mappa dopo il Clear()
.
Avete qualche idea su cosa può causare questa perdita di memoria? Come posso costringere i membri di OC
a distruggere? Forse c'è un qualche tipo di alternativa per OC
? Grazie in anticipo per qualsiasi aiuto!
EDIT:
ho fatto alcuni test con XAML Map Control - https://xamlmapcontrol.codeplex.com/, ed i risultati sono sorprendenti. Le prestazioni complessive della mappa con oltre 1000 elementi aggiunti sono inferiori a quelle native MapControl
, MA, se chiamo Add()
x1000, quindi Clear()
, quindi Add()
x1000, i finalizzatori di PushpinViewModel
vengono chiamati! La memoria viene liberata e l'app non si arresta in modo anomalo. Quindi c'è sicuramente qualcosa di sbagliato con Microsoft MapControl
...
Il problema è probabile che le bitmap si sta caricando al modulo vengono memorizzate nella cache in memoria. La raccolta del GC non la rimuove dalla cache. Vedi [questi] (http://stackoverflow.com/questions/1684489/how-do-you-make-sure-wpf-releases-large-bitmapsource-from-memory) [related] (http://stackoverflow.com)/questions/5530645/releasing-bitmapimages-used-as-image-control-source-memory-problem) domande. –
Grazie per la risposta. Stavo indagando su questa possibilità e sono d'accordo sul fatto che potrebbe davvero essere una ragione. Ho controllato i tuoi link, purtroppo le risposte postate sono per lo più applicate alle applicazioni WPF (sono in una WinRT - App universale). Sono riuscito a caricare un'immagine dal flusso (come un metodo asincrono, ma ha funzionato) - sfortunatamente non ha aiutato. Il consumo di memoria è ancora maggiore: ( – Malutek
Non riesco a parlare con il tuo problema specifico, ma ho riscontrato alcuni problemi con MapItemsControl. Avevo praticamente la stessa configurazione che hai fatto: esegui il bind su ObservableCollection usando MVVM. Ho navigato lontano dalla mappa in un'altra vista, poi di nuovo alla mappa e ripetuto questo avanti e indietro per 3-5 volte, avrei (di solito) ottenuto un errore di "Violazione di accesso" che ho ristretto a MapItemsControl. con un comportamento che prende una fonte di elementi e disegna invece per me simboli sulla mappa, fammi sapere se vuoi che io pubblichi la fonte –