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?

More than 1 year has passed since last update.

GoでヒープやメモリやGCの状態を出力してみた

Last updated at Posted at 2023-10-20

今回のゴール

ヒープやメモリやGCの状態を関数内に配置して、数値で観測してみた。

実装

最終的なコードは以下です。

package main

import (
	"fmt"
	"runtime"
)

// 状態を出力する関数
func printMemStats() {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	// 現在のヒープの状態を出力
	fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
	// ヒープに割り当てられた累計量
	fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
	// システムがGoランタイムに割り当てたメモリ量
	fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
	// そしてガベージコレクションの回数
	fmt.Printf("\tNumGC = %v\n", m.NumGC)
}

// バイトをメガバイトに変換するヘルパー関数
func bToMb(b uint64) uint64 {
	return b / 1024 / 1024
}

// メイン関数
func main() {
	printMemStats()

	// 思い処理なので注意
	data := make([]int, 1<<25) // 2^25要素のスライスを作成
	for i := range data {
		data[i] = i
	}
	printMemStats()
}

// Alloc = 0 MiB   TotalAlloc = 0 MiB      Sys = 6 MiB     NumGC = 0
// Alloc = 256 MiB TotalAlloc = 256 MiB    Sys = 267 MiB   NumGC = 1
  1. func printMemStats() : printMemStatsという名前の関数を定義しています。この関数は引数を取らず、何も返しません。
  2. var m runtime.MemStats : runtime.MemStats型の新しい変数mを宣言しています。この型はGoランタイムが使用しているメモリに関する統計情報を保持します。
  3. runtime.ReadMemStats(&m) : ReadMemStats関数を呼び出して、現在のメモリ統計情報を取得し、それを先ほど宣言した変数mに格納しています。
  4. fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc)) : Allocフィールド(現在ヒープに割り当てられているバイト数)の値をメガバイト単位で表示しています。これはbToMb関数を使用してバイトからメガバイトに変換されます。
  5. fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc)) : 同様に、TotalAllocフィールド(ヒープに割り当てられた累計バイト数)の値も表示しています。
  6. fmt.Printf("\tSys = %v MiB", bToMb(m.Sys)) : Sysフィールド(Goランタイムがシステムから取得した合計メモリ)の値も表示しています。
  7. fmt.Printf("\tNumGC = %v\n", m.NumGC) : 最後に、NumGCフィールド(これまでに実行されたガベージコレクションの回数)の値を表示しています。
  8. func bToMb(b uint64) uint64 { return b / 1024 / 1024 } : バイトをメガバイトに変換するための補助関数bToMbを定義しています。この関数は64ビット符号なし整数を引数に取り、同じく64ビット符号なし整数を返します。
  9. func main() { printMemStats() : メイン関数で、最初にprintMemStats()関数を呼び出して現在のメモリ統計情報を表示します。
  10. data := make([]int, 1<<25) : 2^25要素(約3300万要素)のスライスを作成し、それを変数dataに格納します。これは大量のメモリを消費する処理です。
  11. for i := range data { data[i] = i } : 作成したスライスの各要素にそのインデックスと同じ値を設定します。
  12. printMemStats() }: 最後に再度printMemStats()関数を呼び出して、メモリ消費が多い処理後のメモリ統計情報を表示します。

ヒープとは

  • Goコンパイラでメモリを確保できない値がヒープにエスケープされる。
  • GCされる。
  • Goコンパイラとランタイムによって、いつ使用され、いつクリーンアップされるか仮定できない。
  • サイズが動的に決定される。

スタックとは

  • ローカル変数で、ポインタではないGoの値が割り当てられる。
  • GCされない。
  • GCを使わないので効率的。
  • レキシカルスコープに結びついたメモリが割り当てられる。
  • Goコンパイラが、メモリを確保する。
  • goroutineスタックに格納される。

所感

GC観測できて、すごってなりました。発言できたのは、プログラムが思ったより重い処理だったのでメモリ解放を起こしたのかなと思っています。

スタックも出力できる

Go 製ソフトウェアでメモリ使用量の多い関数を特定する - Cybozu Inside Out | サイボウズエンジニアのブログ

参考記事

Goのスタックとヒープについて - SO Technologies 開発者ブログ

runtime package - runtime - Go Packages

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?