2011-12-05 8 views
5

Probablemente haya 5 o 6 publicaciones SO que tangencialmente toquen esto, pero ninguno realmente responde la pregunta.Registrando el uso de la memoria de un objeto

Tengo un objeto de diccionario que uso como memoria caché para almacenar valores. El problema es que no sé cuán grande es: con el tiempo podría crecer o no, pero no puedo decirlo y, por lo tanto, no puedo medir su efectividad ni sacar conclusiones sobre cómo un usuario está usando el software. . Debido a que esta es una pieza que entrará en producción y supervisará algo durante un período de tiempo muy largo, no tiene sentido adjuntar un perfilador de memoria o algo similar a la depuración.

Idealmente, me gustaría simplemente hacer una llamada en mi temporizador que hacer algo como:

private void someTimer_Tick(object sender, EventArgs e) 
{ 
    ... 
    float mbMem = cacheMap.GetMemorySize(); 
    RecordInLog(DateTime.Now.ToString() + ": mbMem is using " + mbMem.ToString() + "MB of memory"); 
    ... 
} 

se puede hacer esto sin asociar alguna herramienta de depuración para que pueda ser utilizado escenario desplegado?

+2

Quizás ya haya visto esto, pero por las dudas, puede hacer la aproximación de serrialización http://stackoverflow.com/questions/605621/how-to-get-object-size-in-memory – oleksii

+2

Es necesario contar los bytes ? ¿Podría simplemente contar el número de claves o valores únicos ser lo suficientemente bueno? –

+0

@oleksii Esta podría ser la única solución. Todavía estoy tratando de encontrar la manera de evitar el golpe de rendimiento sustancial que recibes cuando serializas cosas (es un gran éxito). – kmarks2

Respuesta

1

Dado su comentario más reciente, que el valor es una cadena de longitud variable, debería ser fácil calcular el tamaño de cada elemento en el diccionario. Consideraría ahorrar tiempo y esfuerzo creando su propio objeto de almacenamiento en caché (posiblemente solo envolviendo un diccionario) y haciendo un seguimiento del tamaño total a medida que se agregan y eliminan los elementos del caché. De esta forma, en cualquier punto del tiempo puede decir el tamaño total de los valores en la memoria caché mirando el valor que ha estado siguiendo todo el tiempo.

Si necesita que su caché para exponer la funcionalidad completa IDictionary, se podría implementar la interfaz, delegando hasta el diccionario "real" y modificar el valor del tamaño acumulado de las operaciones y AddRemove. Si no necesita su caché para exponer la funcionalidad completa de IDictionary, simplemente defina una interfaz simplificada (con quizás solo Add, Contains y Remove y una propiedad CumulativeSize). O bien, podría decidir implementar un objeto de caché sin una interfaz . Si se tratara de mí, yo tampoco utilizo IDictionary o definir una interfaz, como ICache

Así, la memoria caché podría ser algo como esto (sin compilar y no probado):.

public interface ICacheWithCumulativeSize 
{ 
    void Add(string key, string value); 
    bool Contains(string key); 
    void Remove(string key); 
    int CumulativeSize { get; } 
} 

public class MyCache : ICacheWithCumulativeSize 
{ 
    private IDictionary<string, string> dict = new Dictionary<string, string>(); 

    public void Add(string key, string value) 
    { 
    CumulativeSize += value.Length; 
    dict[key] = value; 
    } 

    public bool Contains(string key) 
    { 
    return dict.ContainsKey(key); 
    } 

    public void Remove(string key) 
    { 
    string toRemove = dict[key]; 
    CumulativeSize -= value.Length; 
    dict.Remove(key); 
    } 

    int CumulativeSize { public get; private set; } 
} 

esto es bastante duro. Obviamente, podría ser más eficiente y más robusto. No estoy haciendo cualquier verificación en Add y Remove para ver si ya existe una clave, etc., pero creo que probablemente entiendas la idea. Además, es posible que las cadenas que se almacenan como valores en el diccionario se puedan modificar externamente (tal vez no en su programa, pero teóricamente), por lo que la longitud de una cadena cuando se resta del CumulativeSize cuando se elimina un valor de la memoria caché puede no ser la misma que la longitud de esa cadena cuando se agregó originalmente. Si esto es una preocupación, podría considerar almacenar copias de los valores en el diccionario interno. No sé lo suficiente sobre su aplicación para decir si esta es una buena idea o no.

Para completar ...Aquí hay una implementación aproximada que simplemente envuelve un diccionario, expone la interfaz IDictionary y realiza un seguimiento del tamaño total de los elementos en la memoria caché. Tiene un código un poco más defensivo, principalmente para proteger el acumulador de tamaño. La única parte que podría considerar engañosa es el conjunto de índices ... Mi implementación comprueba si el índice que se está configurando ya existe. Si es así, el valor acumulado se disminuye de forma adecuada y luego se incrementa en función del tamaño del valor de entrada. De lo contrario, creo que es bastante sencillo.

public class MySpecialDictionary : IDictionary<string, string> 
    { 
    private IDictionary<string, string> dict = new Dictionary<string, string>(); 

    public int TotalSize { get; private set; } 

    #region IDictionary<string,string> Members 

    public void Add(string key, string value) 
    { 
     dict.Add(key, value); 
     TotalSize += string.IsNullOrEmpty(value) ? 0 : value.Length; 
    } 

    public bool ContainsKey(string key) 
    { 
     return dict.ContainsKey(key); 
    } 

    public ICollection<string> Keys 
    { 
     get { return dict.Keys; } 
    } 

    public bool Remove(string key) 
    { 
     string value; 
     if (dict.TryGetValue(key, out value)) 
     { 
     TotalSize -= string.IsNullOrEmpty(value) ? 0 : value.Length; 
     } 
     return dict.Remove(key); 
    } 

    public bool TryGetValue(string key, out string value) 
    { 
     return dict.TryGetValue(key, out value); 
    } 

    public ICollection<string> Values 
    { 
     get { return dict.Values; } 
    } 

    public string this[string key] 
    { 
     get 
     { 
     return dict[key]; 
     } 
     set 
     { 
     string v; 
     if (dict.TryGetValue(key, out v)) 
     { 
      TotalSize -= string.IsNullOrEmpty(v) ? 0 : v.Length; 
     } 
     dict[key] = value; 
     TotalSize += string.IsNullOrEmpty(value) ? 0 : value.Length; 
     } 
    } 

    #endregion 

    #region ICollection<KeyValuePair<string,string>> Members 

    public void Add(KeyValuePair<string, string> item) 
    { 
     dict.Add(item); 
     TotalSize += string.IsNullOrEmpty(item.Value) ? 0 : item.Value.Length; 
    } 

    public void Clear() 
    { 
     dict.Clear(); 
     TotalSize = 0; 
    } 

    public bool Contains(KeyValuePair<string, string> item) 
    { 
     return dict.Contains(item); 
    } 

    public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) 
    { 
     dict.CopyTo(array, arrayIndex); 
    } 

    public int Count 
    { 
     get { return dict.Count; } 
    } 

    public bool IsReadOnly 
    { 
     get { return dict.IsReadOnly; } 
    } 

    public bool Remove(KeyValuePair<string, string> item) 
    { 
     string v; 
     if (dict.TryGetValue(item.Key, out v)) 
     { 
     TotalSize -= string.IsNullOrEmpty(v) ? 0 : v.Length; 
     } 
     return dict.Remove(item); 
    } 

    #endregion 

    #region IEnumerable<KeyValuePair<string,string>> Members 

    public IEnumerator<KeyValuePair<string, string>> GetEnumerator() 
    { 
     return dict.GetEnumerator(); 
    } 

    #endregion 

    #region IEnumerable Members 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return dict.GetEnumerator(); 
    } 

    #endregion 
    } 

¡Buena suerte!

+0

Recibí tu primer párrafo solo antes de marcar esto como la solución. La solución, sin importar cómo se implemente, necesita mantener una suma continua para ser efectiva. Es tan clara y sencilla la respuesta correcta que me estoy enfrentando a mí misma que no pensé en ello por mi cuenta. Gracias de nuevo. – kmarks2

1

No hay ningún método en el marco para indicarle cuánta memoria está usando un objeto, porque hacer eso para cualquier tipo de objeto sería muy complicado.

Si sabe que su diccionario es fácil de crear, es decir, no contiene referencias duplicadas para el mismo artículo, y el uso de memoria de cada elemento puede ser aproximado fácilmente, puede recorrer el diccionario y resumir el tamaño aproximado de cada objeto.

+0

Sí, las llaves son únicas. Esta fue mi alternativa: solo estimar el tamaño del par k/v y escalar ese valor como el tamaño de la colección. Mi principal problema es que el valor en el par es una cadena de longitud variable, y una vez que rompo 1 millón de registros, ese +/- 50% de margen de maniobra en el valor puede ser grande. – kmarks2

+1

Para cadenas, el uso de la memoria suele ser simple, con una longitud * 2 más cierta sobrecarga (12/20 bytes dependiendo de la plataforma de 32/64 bits). Sin embargo, tenga en cuenta que si crea cadenas usando un 'StringBuilder', la cadena puede contener un espacio extra no utilizado al final. – Guffa

+0

Esto es un pensamiento ... Voy a intentar esto y ver si el hecho de que hay 1-2 millones de entradas es un factor decisivo para el rendimiento. – kmarks2

Cuestiones relacionadas