Ir a notas de estudio (75) -memoria de pila, memoria de pila, análisis de escape

Transferencia desde:
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=536#/detail/pc?id=5247

Para permitir que los programadores se concentren mejor en la implementación del código comercial, el Golenguaje agrega un mecanismo de recolección de basura para recuperar automáticamente la memoria que ya no se usa. GoEl lenguaje tiene dos partes de espacio de memoria: memoria de pila y memoria de pila.

1. Pila de memoria

La pila solo permite poner datos en un extremo de la tabla lineal y luego sacar los datos de este extremo, en el orden LIFO (último en entrar, primero en salir), como se muestra en la figura.
Pila de memoria
El proceso de colocar elementos en la pila se llama empujar. Empujar la pila aumentará el número de elementos en la pila. El último elemento colocado siempre está en la parte superior de la pila, y el elemento colocado primero siempre está en la parte inferior de la pila.

Al quitar elementos de la pila, solo se pueden quitar de la parte superior de la pila. Después de eliminar los elementos, se reducirá el número de pilas. El elemento que se coloca en primer lugar siempre se elimina en último lugar, y el elemento que se coloca en último lugar siempre se elimina en primer lugar. No se permite obtener datos de la parte inferior de la pila, ni ver o modificar ningún miembro de la pila (miembros que no sean la parte superior de la pila).

El compilador asigna y libera automáticamente la memoria de pila, y el desarrollador no puede controlarla. La memoria de pila generalmente almacena en la función variables locales, parámetros, etc. Cuando se crea la función, estas memorias se crean automáticamente, cuando la función regresa, estas memorias se liberan automáticamente.

La pila se puede utilizar para la asignación de memoria, la asignación y recuperación de la pila es muy rápida. El siguiente código muestra el papel de las ramas en la asignación de memoria, el código es el siguiente:

func calc(a, b int) int {
    
    
	var c int
	c = a * b

	var x int
	x = c * 10

	return x
}

El código anterior se optimiza sin ninguna circunstancia, se realizará cy se le xasignará una variable de proceso. GoSegún el idioma predeterminado cy xasignado en la pila, estas dos variables calc()ya no se utilizarán cuando la función salga, el final de la función, guarde cy xapile la memoria y luego libere la memoria de la pila, el proceso de asignación de memoria a través de toda la asignación de la pila y El reciclaje será muy rápido.

2. Memoria de pila

La asignación de memoria apilada es similar a colocar varios muebles en una habitación, y el tamaño de los muebles varía.

Al asignar memoria, debe encontrar un espacio suficiente para sostener los muebles antes de colocarlos. Después de colocar y vaciar repetidamente los muebles, el espacio de la habitación se volverá desordenado. En este momento, existirá la colocación de muebles en el espacio. Aunque hay suficiente espacio, los espacios se distribuyen en diferentes áreas, y no hay espacio continuo. Llegue al problema de la colocación de muebles.

En este momento, el asignador de memoria necesita ajustar y optimizar estos espacios, como se muestra en la figura.

Memoria del montónEn comparación con la memoria asignada por el montón y la memoria asignada por la pila, el montón es adecuado para la asignación de memoria de tamaño impredecible. Pero el precio que se paga por esto es una menor velocidad de asignación y una fragmentación de la memoria.

El ciclo de vida de la memoria de pila es más largo que la memoria de pila. Si el valor devuelto por la función se usará en otro lugar, el compilador asignará automáticamente este valor al montón. En comparación con la memoria de pila, la memoria de pila no puede ser liberada automáticamente por el compilador y solo puede ser liberada por el recolector de basura, por lo que la eficiencia de la memoria de pila será muy alta.

2. Análisis de escape

Dado que la memoria de pila es más eficiente, la memoria de pila debe usarse primero. Entonces Go, ¿el lenguaje es cómo se debe asignar un juez a la variable en el montón o apilarla? Esto requiere un análisis de escape. A continuación, utilizo un ejemplo para explicar el análisis de escape, el código es el siguiente:

package main

func main() {
    
    
    newString()
}

func newString() *string{
    
    
   s:=new(string)
   *s = "wohu"
   return s
}

Ahora uso el análisis de escape para ver si se ha producido un escape. El comando es el siguiente:

wohu@ubuntu:~/gocode/src$ go build -gcflags="-m -l" demo.go
# command-line-arguments
./demo.go:12:10: new(string) escapes to heap
wohu@ubuntu:~/gocode/src$ 
  • -m Medios para imprimir la información del análisis de escape;
  • -l Indica que la alineación está prohibida y la fuga se puede observar mejor;

Se puede ver en la salida anterior que se ha producido un escape, lo que significa que cuando se utiliza un puntero como valor de retorno de una función, debe producirse un escape.

Las variables que escapan a la memoria del montón no se pueden reciclar inmediatamente. Solo se pueden borrar con marcas de recolección de basura, lo que aumenta la presión de la recolección de basura. Por lo tanto, evite escapar tanto como sea posible y asigne variables en la memoria de la pila para que los recursos puedan ser reciclado cuando la función vuelve., Mejorar la eficiencia.

Aquí la newStringfunción I ha sido optimizada para evitar el código optimizado de la función de escape de la siguiente manera:

func newString() string{
    
    
   s:=new(string)
   *s = "wohu"
   return *s
}

Vea el análisis de escape del código anterior a través del comando nuevamente, el comando es el siguiente:

wohu@ubuntu:~/gocode/src$ go build -gcflags="-m -l" demo.go
# command-line-arguments
./demo.go:8:10: newString new(string) does not escape
wohu@ubuntu:~/gocode/src$ 

A través de los resultados del análisis, podemos ver que aunque la variable de puntero todavía está declarada s, la función no devuelve el puntero, por lo que no se produce ningún escape.

El análisis de escape es un método para juzgar si las variables se asignan en el montón o en la pila. En proyectos reales, el escape debe evitarse tanto como sea posible, para no ser ralentizado por el GC, mejorando así la eficiencia.

Sugerencias: desde el punto de vista del análisis de escape, aunque los punteros pueden reducir la copia de memoria, también pueden causar fugas, por lo que debe elegir si desea utilizar punteros de acuerdo con la situación real.

Habilidades de optimización

  • Evite los escapes tanto como sea posible, porque la memoria de la pila es más eficiente, por lo que no es necesaria GC. Por ejemplo, parámetro que pasa objetos pequeños, arraymejor que slicebuen efecto.

  • Si no se puede evitar el escape y la memoria se asigna en el montón, entonces, para las operaciones frecuentes de la aplicación de memoria, debemos aprender a reutilizar la memoria, como el uso sync.Pool.

  • Elija los algoritmos adecuados para lograr un alto rendimiento, como el espacio para el tiempo.

Sugerencias: al optimizar el rendimiento, debe combinar las pruebas comparativas para verificar si su optimización ha mejorado.

Lo anterior se basa en la Gohabilidad resumida en el lenguaje del mecanismo de gestión de la memoria en tres direcciones, basándose en estas tres direcciones generales básicamente se puede optimizar el efecto que se desea. Además, hay algunos consejos para evitar tanto como sea posible, como el uso de bloqueos, la concurrencia bloqueada lo más estrechamente posible, el uso de StringBuildermake stringy [ ] byteconversion between, deferanidando no demasiado.

El último recomendó una Goherramienta que viene con el análisis de rendimiento del lenguaje pprof, a través del cual se puede visualizar el CPUanálisis, análisis de memoria, análisis de obstrucciones, análisis de mutex.

Supongo que te gusta

Origin blog.csdn.net/wohu1104/article/details/113815428
Recomendado
Clasificación