Notas prácticas de Go Language (26) | Disposición de la memoria del paquete Go inseguro

Inseguro, como su nombre indica, no es seguro. Go también define el nombre de este paquete. Intentemos no usarlo tanto como sea posible. Si lo usa y ve el nombre, pensará en no usarlo tanto como sea posible , o tenga más cuidado. Úselo.

Aunque este paquete no es seguro, también tiene sus ventajas, es decir, puede omitir el mecanismo de seguridad de la memoria de Go y leer y escribir directamente en la memoria. Por lo tanto, a veces, debido a necesidades de rendimiento, correrá algunos riesgos al usar este paquete para realizar operaciones de memoria. .operando.

Tamaño de la función

SizeofLa función puede devolver el tamaño de la memoria ocupada por un tipo. Este tamaño solo está relacionado con el tipo y no tiene nada que ver con el tamaño del almacenamiento de la variable correspondiente al tipo. Por ejemplo, el tipo bool ocupa un byte, y int8 también ocupa un byte.

func main() {
	fmt.Println(unsafe.Sizeof(true))
	fmt.Println(unsafe.Sizeof(int8(0)))
	fmt.Println(unsafe.Sizeof(int16(10)))
	fmt.Println(unsafe.Sizeof(int32(10000000)))
	fmt.Println(unsafe.Sizeof(int64(10000000000000)))
	fmt.Println(unsafe.Sizeof(int(10000000000000000)))
}
   

Para los tipos enteros, el número de bytes ocupados significa el tamaño del rango de números almacenados por este tipo. Por ejemplo, int8 ocupa un byte, que es de 8 bits, por lo que el rango de tamaño que puede almacenar es -128 ~~ 127, que es −2 ^ (n-1) a 2 ^ (n-1) -1, n significa bit, int8 significa 8bit, int16 significa 16bit, y así sucesivamente.

Para el tipo int relacionado con la plataforma, esto depende de si la plataforma es de 32 bits o de 64 bits, y se toma la más grande. Por ejemplo, cuando lo pruebo yo mismo, la salida anterior encontrará que los tamaños de int e int64 son los mismos porque mi computadora es una plataforma de 64 bits.

func Sizeof(x ArbitraryType) uintptr
   

Lo anterior es Sizeofla definición de la función, que recibe un ArbitraryTypetipo de parámetro y devuelve un uintptrtipo de valor. Aquí ArbitraryTypeno te preocupes, es solo un marcador de posición. El tipo se exporta por consideraciones de documentación, pero generalmente no se usa. Solo necesitamos saber que representa cualquier tipo, es decir, nuestra función puede recibir cualquier tipo de datos .

// ArbitraryType is here for the purposes of documentation only and is not actually
// part of the unsafe package. It represents the type of an arbitrary Go expression.
type ArbitraryType int
   

Función Alignof

AlignofDevuelve un tipo de valor de alineación, que también se puede llamar factor de alineación o múltiple de alineación. El valor de alineación es un valor relacionado con la alineación de la memoria. Una alineación de memoria razonable puede mejorar el rendimiento de la lectura y escritura de la memoria. Para conocer la alineación de la memoria, consulte los documentos relacionados, que no se presentarán aquí.

   

A partir del resultado del ejemplo anterior, se puede ver que el valor de alineación es generalmente 2 ^ n, y el máximo no excederá de 8 (por esa razón, consulte las reglas de alineación de memoria a continuación). AlignofLa definición de función Sizeofes básicamente la misma. Cabe señalar aquí que los resultados de la computadora de cada persona pueden ser diferentes, similares entre sí.

func Alignof(x ArbitraryType) uintptr
   

Además, la función del paquete de reflexión también se puede utilizar para obtener el valor de alineación, es decir: unsafe.Alignof(x)equivalente a reflect.TypeOf(x).Align().

Función de compensación

OffsetofLa función solo es aplicable al desplazamiento del campo en la estructura de la estructura en relación con la ubicación de memoria de la estructura. El desplazamiento del primer campo de la estructura es 0.

func main() {
	var u1 user1

	fmt.Println(unsafe.Offsetof(u1.b))
	fmt.Println(unsafe.Offsetof(u1.i))
	fmt.Println(unsafe.Offsetof(u1.j))
}


type user1 struct {
	b byte
	i int32
	j int64
}
   

El desplazamiento del campo es la posición inicial del campo en el diseño de memoria de la estructura de la estructura (el índice de ubicación de memoria comienza desde 0). De acuerdo con el desplazamiento del campo, podemos ubicar los campos de la estructura, y luego podemos leer y escribir los campos de la estructura, incluso si son privados, se siente como un hacker. El concepto de compensación, lo introduciremos en detalle en el próximo resumen.

Además, es unsafe.Offsetof(u1.i)equivalente areflect.TypeOf(u1).Field(i).Offset

Interesante tamaño de estructura

Definimos una estructura, esta estructura tiene 3 campos, sus tipos son byte, int32y int64, pero el orden de estos tres campos se puede organizar arbitrariamente, por lo que de acuerdo con el orden, hay un total de 6 combinaciones.

type user1 struct {
	b byte
	i int32
	j int64
}

type user2 struct {
	b byte
	j int64
	i int32
}

type user3 struct {
	i int32
	b byte
	j int64
}

type user4 struct {
	i int32
	j int64
	b byte
}

type user5 struct {
	j int64
	b byte
	i int32
}

type user6 struct {
	j int64
	i int32
	b byte
}
   

Según estas 6 combinaciones, se definen 6 estructuras, que son usuario1, usuario2, ..., usuario 6. Ahora, adivinemos cuánta memoria ocupan estos 6 tipos de estructuras, ese es unsafe.Sizeof()el valor.

Puede adivinar 1 + 4 + 8 = 13, porque el tamaño del byte es 1, el tamaño de int32 es 4 y el tamaño de int64 es 8, y la estructura es en realidad una combinación de campos, por lo que es normal adivinar que el tamaño de la estructura es la suma de los tamaños de los campos.

Pero, sin embargo, puedo decir claramente que esto está mal.

Debido a que hay alineación de memoria y el compilador usa alineación de memoria, el resultado del tamaño final es diferente. Ahora verificamos formalmente los valores de estos tipos de estructuras.

func main() {
	var u1 user1
	var u2 user2
	var u3 user3
	var u4 user4
	var u5 user5
	var u6 user6

	fmt.Println("u1 size is ",unsafe.Sizeof(u1))
	fmt.Println("u2 size is ",unsafe.Sizeof(u2))
	fmt.Println("u3 size is ",unsafe.Sizeof(u3))
	fmt.Println("u4 size is ",unsafe.Sizeof(u4))
	fmt.Println("u5 size is ",unsafe.Sizeof(u5))
	fmt.Println("u6 size is ",unsafe.Sizeof(u6))
}
   

Como puede ver en la salida anterior, el resultado es:

u1 size is  16
u2 size is  24
u3 size is  16
u4 size is  24
u5 size is  16
u6 size is  16
   

Salió el resultado (el resultado de mi computadora, Mac64 bit, el tuyo puede ser diferente), 4 16 bytes, 2 24 bytes, ni igual ni igual, esto muestra:

  1. La alineación de la memoria afecta el tamaño de la estructura
  2. El orden de campo de la estructura afecta el tamaño de la estructura

Combinando los dos puntos anteriores, podemos saber que diferentes órdenes de campo determinan en última instancia el tamaño de la memoria de la estructura, por lo que a veces un orden de campo razonable puede reducir la sobrecarga de memoria .

La alineación de la memoria afectará la huella de memoria de la estructura Ahora analizaremos en detalle por qué el orden diferente de las definiciones de campo hará que la huella de memoria de la estructura sea diferente.

Antes de analizar, veamos las reglas de alineación de la memoria:

  1. Para tipos específicos, el valor de alineación = min (el valor de alineación predeterminado del compilador, tamaño de letra Sizeof length) . Es decir, entre el valor de alineación predeterminado y la huella de memoria del tipo, el valor mínimo es el valor de alineación del tipo. Mi computadora tiene un valor predeterminado de 8, por lo que el valor máximo no excederá de 8.
  2. Después de que cada campo de la estructura se alinea en la memoria, también debe alinearse, valor de alineación = min (valor de alineación predeterminado, longitud máxima del tipo de campo) . Este artículo también es fácil de entender. Entre todos los campos de la estructura, se toma el tipo más pequeño entre la longitud del tipo más grande y el valor de alineación predeterminado.

Las dos reglas anteriores deben entenderse bien y solo entonces se puede analizar la siguiente estructura de estructura. Aquí nuevamente, el valor de alineación también se denomina coeficiente de alineación, múltiplo de alineación y módulo de alineación. Esto significa que el desplazamiento de cada campo en la memoria es un múltiplo del valor de alineación .

Sabemos que los valores de alineación de byte, int32 e int64 son 1, 4 y 8, respectivamente, y el tamaño de la memoria también es 1, 4 y 8. Entonces, para la primera estructura user1, su orden de campo es byte, int32, int64, primero usamos la primera regla de alineación de memoria para realizar la alineación de memoria, y su estructura de memoria es la siguiente.

bxxx|iiii|jjjj|jjjj

user1Escriba, el primer byte de campo, valor de alineación 1, tamaño 1, por lo que se coloca en la primera posición en el diseño de la memoria.

El segundo campo es int32, el valor de alineación es 4 y el tamaño es 4, por lo que su valor de compensación de memoria debe ser un múltiplo de 4. En el actual user1, no puede comenzar desde la segunda posición y debe comenzar desde la quinta posición , que es el desplazamiento La cantidad de cambio es 4. Los bits 2, 3 y 4 los llena el compilador y generalmente tienen el valor 0, que también se denomina agujero de memoria. Entonces, los bits del quinto al octavo son el segundo campo i.

En el tercer campo, el valor de alineación es 8 y el tamaño también es 8. Debido a que los user1dos primeros campos se clasificaron en la octava posición, el desplazamiento del siguiente es exactamente 8, que es un múltiplo del valor de alineación del tercer campo. Sin relleno, puede organizar el tercer campo directamente, es decir, del noveno bit al dieciséis bit es el tercer campo j.

 

Ahora que la longitud de la memoria es de 16 bytes después de la primera regla de alineación de memoria, comenzamos a usar la segunda regla de alineación de memoria para la alineación. Según la segunda regla, el valor de alineación predeterminado es 8, y la longitud máxima de tipo en el campo también es 8, por lo que se calcula el valor de alineación de la estructura. Nuestra longitud de memoria actual es 16, que es un múltiplo de 8, y Se ha logrado la alineación.

Hasta ahora, user1la huella de memoria de la estructura es de 16 bytes.

Ahora analizamos otro user2tipo, su tamaño es 24, pero se cambia el orden de los campos i y j, ocupa 8 bytes, veamos por qué? O primero use nuestro análisis de la primera regla de memoria.

bxxx|xxxx|jjjj|jjjj|iiii

Según el valor de alineación y el tamaño que ocupa, el primer campo b offset es 0, ocupa 1 byte y se coloca en la primera posición.

El segundo campo j es int64, el valor de alineación y el tamaño son 8, así que comience desde el desplazamiento 8, es decir, los bits del 9 al 16 son j, lo que significa que el compilador rellena los bits del 2 al 8.

En la actualidad, todo el diseño de la memoria se ha desplazado en 16 bits, que es exactamente un múltiplo de 4 del valor de alineación del tercer campo i, por lo que se puede organizar directamente sin relleno. Los bits 17 al 20 son i.

 

Ahora que todos los campos están alineados, el tamaño total de la memoria es 1 + 7 + 8 + 4 = 20 bytes. Comenzamos a usar la segunda regla de alineación de memoria, que es la alineación de la estructura, mediante el valor de alineación predeterminado y el Tamaño de campo más grande, encuentre el valor de alineación de la estructura en 8.

Ahora nuestro tamaño de diseño de memoria completo es 20, no un múltiplo de 8, por lo que necesitamos llenar la memoria para hacer un múltiplo de 8, el más pequeño es 24, por lo que el diseño de memoria completo después de la alineación es

bxxx|xxxx|jjjj|jjjj|iiii|xxxx

Así que esta es la razón por la que finalmente obtuvimos user2un tamaño de 24. Con base en el método anterior, podemos obtener el diseño de memoria de varias otras estructuras.

usuario3

iiii|bxxx|jjjj|jjjj

usuario4

iiii|xxxx|jjjj|jjjj|bxxx|xxxx

usuario5

jjjj|jjjj|bxxx|iiii

usuario6

jjjj|jjjj|iiii|bxxx

La respuesta se da arriba, puede consultarla user1y user2probarla cuando la presione . En el próximo artículo, presentaremos el funcionamiento de la memoria a través del puntero inseguro y la lectura y escritura de la memoria.

Supongo que te gusta

Origin blog.csdn.net/qq_32907195/article/details/112464102
Recomendado
Clasificación