0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Goのデータ型って実際何バイト?サンプルコードでメモリサイズを確認する

Posted at

はじめに

Goを使っていて、「この変数って、実際にはどれくらいメモリを使っているんだろう?」と疑問を感じたことはありませんか?

この記事ではGoの各データ型が実際にどのくらいのメモリ領域を確保するのか、具体的なサンプルコードと実行結果を交えて検証していきます。

型サイズを把握しておくことで、不要なメモリ消費を抑え、より効率的なコードが書けるようになるはずです。

なお、この記事におけるGoのバージョンはgo version go1.24.1 darwin/amd64です。

【Go】データ型別に確保されるメモリ領域まとめ

Goでは格納する値の内容に関わらず、型ごとに確保されるメモリ領域が決まっています。

ここでは、unsafeパッケージのSizeof関数を使って各型のサイズを検証していきましょう。

プリミティブ型

まずは基本的なデータ型(プリミティブ型)から見ていきます。

main.go
package main

import (
	"fmt"
	"unsafe"
)

func main() {
	fmt.Printf("byte: %dバイト\n", unsafe.Sizeof(byte(0)))
	fmt.Printf("uint: %dバイト\n", unsafe.Sizeof(uint(0)))
	fmt.Printf("uint8: %dバイト\n", unsafe.Sizeof(uint8(0)))
	fmt.Printf("uint16: %dバイト\n", unsafe.Sizeof(uint16(0)))
	fmt.Printf("uint32: %dバイト\n", unsafe.Sizeof(uint32(0)))
	fmt.Printf("uint64: %dバイト\n", unsafe.Sizeof(uint64(0)))
	fmt.Printf("uintptr: %dバイト\n", unsafe.Sizeof(uintptr(0)))
	fmt.Printf("int: %dバイト\n", unsafe.Sizeof(0))
	fmt.Printf("int8: %dバイト\n", unsafe.Sizeof(int8(0)))
	fmt.Printf("int16: %dバイト\n", unsafe.Sizeof(int16(0)))
	fmt.Printf("int32: %dバイト\n", unsafe.Sizeof(int32(0)))
	fmt.Printf("int64: %dバイト\n", unsafe.Sizeof(int64(0)))
	fmt.Printf("float32: %dバイト\n", unsafe.Sizeof(float32(0)))
	fmt.Printf("float64: %dバイト\n", unsafe.Sizeof(float64(0)))
	fmt.Printf("complex64: %dバイト\n", unsafe.Sizeof(complex64(0)))
	fmt.Printf("complex128: %dバイト\n", unsafe.Sizeof(complex128(0)))
	fmt.Printf("string: %dバイト\n", unsafe.Sizeof(""))
	fmt.Printf("rune: %dバイト\n", unsafe.Sizeof('a'))
	fmt.Printf("bool: %dバイト\n", unsafe.Sizeof(false))
}

実行結果は次のとおりです。

go-sample $ go run main.go
byte: 1バイト
uint: 8バイト
uint8: 1バイト
uint16: 2バイト
uint32: 4バイト
uint64: 8バイト
uintptr: 8バイト
int: 8バイト
int8: 1バイト
int16: 2バイト
int32: 4バイト
int64: 8バイト
float32: 4バイト
float64: 8バイト
complex64: 8バイト
complex128: 16バイト
string: 16バイト
rune: 4バイト
bool: 1バイト

array

arrayは要素数と要素の型サイズによって確保するメモリ領域が変わります。

main.go
package main

import (
	"fmt"
	"unsafe"
)

func main() {
	fmt.Printf("array1: %dバイト\n", unsafe.Sizeof([1]int{1}))
	fmt.Printf("array2: %dバイト\n", unsafe.Sizeof([3]int8{1, 2, 3}))
	fmt.Printf("array3: %dバイト\n", unsafe.Sizeof([5]string{"a", "b", "c", "d", "e"}))
}

実行結果は下記のとおりです。

go-sample $ go run main.go
array1: 8バイト
array2: 3バイト
array3: 80バイト

このように、要素の型サイズに要素数をかけ合わせた分が、arrayの型サイズとなるのです。

slice, map, chan

slice, map, chanは要素の型サイズや要素数にかかわらず、固定の型サイズになります。

main.go
	fmt.Printf("slice1: %dバイト\n", unsafe.Sizeof([]int{1}))
	fmt.Printf("slice2: %dバイト\n", unsafe.Sizeof([]int8{1, 2, 3}))
	fmt.Printf("slice3: %dバイト\n", unsafe.Sizeof([]string{"a", "b", "c", "d", "e"}))
	fmt.Printf("map1: %dバイト\n", unsafe.Sizeof(map[string]int{"a": 1}))
	fmt.Printf("map2: %dバイト\n", unsafe.Sizeof(map[string]int8{"a": 1, "b": 2, "c": 3}))
	fmt.Printf("map3: %dバイト\n", unsafe.Sizeof(map[int]bool{1: true, 2: false, 3: true, 4: false, 5: true}))
	fmt.Printf("chan1: %dバイト\n", unsafe.Sizeof(make(chan int, 1)))
	fmt.Printf("chan2: %dバイト\n", unsafe.Sizeof(make(chan int8, 3)))
	fmt.Printf("chan3: %dバイト\n", unsafe.Sizeof(make(chan string, 5)))

実行結果は次のとおりです。

go-sample $ go run main.go
slice1: 24バイト
slice2: 24バイト
slice3: 24バイト
map1: 8バイト
map2: 8バイト
map3: 8バイト
chan1: 8バイト
chan2: 8バイト
chan3: 8バイト

sliceは24バイト、mapは8バイト、chanは8バイトで固定されていることがわかります。

構造体

構造体の型サイズは各フィールドの型サイズの合計となります。

サンプルコードで確認します。

main.go
package main

import (
	"fmt"
	"unsafe"
)

type A struct {
	i int     // 8バイト
	s string  // 16バイト
	f float64 // 8バイト
}

type B struct {
	i int // 8バイト
	a A   // 8 + 16 + 8 = 32バイト
}

func main() {
	a := A{}
	fmt.Printf("A: %dバイト\n", unsafe.Sizeof(a))
	b := B{}
	fmt.Printf("B: %dバイト\n", unsafe.Sizeof(b))
}

実行結果は次のとおりです。

go-sample $ go run main.go
A: 32バイト
B: 40バイト

構造体A, Bともに各フィールドの型サイズの合計であることがわかります。

※あ

ポインタ

64bitマシンの場合、ポインタの型サイズは常に8バイトになります。

main.go
package main

import (
	"fmt"
	"unsafe"
)

type A struct {
	i int
	s string
	f float64
}

type B struct {
	i int
	a A
}

func main() {
	a := A{}
	fmt.Printf("A: %dバイト\n", unsafe.Sizeof(&a))
	b := B{}
	fmt.Printf("B: %dバイト\n", unsafe.Sizeof(&b))
}

実行結果は次のとおりです。

go-sample $ go run main.go
A: 8バイト
B: 8バイト

interface

interfaceは常に固定で16バイトのメモリ領域を確保します。

main.go
package main

import (
	"fmt"
	"unsafe"
)

type MultiMethod interface {
	MethodA() string
	MethodB() int
	MethodC(float64) bool
}

type X struct{}

func (x X) MethodA() string {
	return "MethodA"
}

func (x X) MethodB() int {
	return 42
}

func (x X) MethodC(f float64) bool {
	return f > 0
}

func main() {
	var m MultiMethod = X{}
	fmt.Printf("MultiMethodを格納したインターフェース: %dバイト\n", unsafe.Sizeof(m))
}

実行結果は次のとおりです。

go-sample $ go run main.go
MultiMethodを格納したインターフェース: 16バイト

おわりに

この記事では、Goの主要なデータ型がどのようにメモリ領域を確保するかを実例を交えて紹介しました。

メモリ消費を意識する場面ではunsafe.Sizeofを活用して、実際のサイズを確認してみてください。

記事の内容に誤りがあれば、コメントにて教えていただけますと幸いです。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?