golangエスケープ解析

私たちのプログラムを書くための言語とのGCは大きな利便性をもたらしていますが、低レベルの詳細の多くを遮蔽すると同時に、オブジェクトなどが割り当てられているか、スタック上のヒープに割り当てられました。それはそんなに、しかしとして強迫性障害のプログラム猿を気に、または最適なパフォーマンスのためのコードを記述するために自分自身を作るにしたくないコードのために一般的であるが、我々はまだエスケープが何であるかを知っておく必要があり、そして脱出が発生したかどうかを決定する方法。

ヒープとスタックとは何ですか?

あなたが知っている必要がまず第一に、私たちは、ヒープとスタックが何であるかについて話しています。これは、「ヒープ」と「スタック」が、内部のオペレーティングシステムの概念内部データ構造ではありません。

スタック

プログラムでは、各機能ブロックは、独自のローカル変数ストレージに(メモリフットプリント未満)、リターンアドレス、戻りデータ型の値、特定の構造及びアドレッシングを有するメモリ領域を独自のメモリ領域を有するであろうサイズはコンパイル時に決定された、それはまた、非常に迅速に非常にわずかなオーバーヘッドを取り上げています。このアドレスは、メモリ・スタックと呼ばれています。スタックは、スレッドレベルであり、サイズは作成時に決定されているので、データが大きすぎて、彼らは意志「スタックオーバーフロー」が発生したとき。

ヒープ

スタックローカル変数がある場合、プログラムは、グローバル変数、ローカル変数大きなメモリフットプリントでは、エスケープが発生し、このメモリは、特定の構造ではなく、決まったサイズが存在しない、必要に応じて、調整することができます。要するに、内部ヒープがある場合に格納されるデータがたくさんあります。ヒープは、プロセスレベルです。可変時間は、ヒープに割り当てられる必要がある場合、コストはGCの言語で行くことに、比較的大きくなり、圧力がGCを高めるだけでなく、簡単にメモリの断片化につながります。

なぜ、いくつかの変数はヒープに割り当てされるべきである、といくつかは、スタック上に割り当てられますか?

C ++でのスタートから問題。C ++では、我々は次のコードがあるとします。

`` C ++
INT * F1(){
int型I = 5。
返す&I;
}

メインINT(){
int型I = F1()。
I = 6;
0を返します。
}


这时候程序结果是无法预期的,因为在函数f1中,i是一个局部变量,会分配在栈上,而栈在函数返回之后就失效了(Plan9 汇编中SP指针被修改),于是i的地址所存的值是不可预期的,后续在main中对返回的i的地址中的值的修改可能会修改掉程序运行的数据,造成结果无法预期。

所以对于需要返回一个地址回去的情况,在C++中需要用new来分配一块堆上的内存才行,因为堆是进程级别的,也就是全局的,除非程序猿手动释放,否则不会被回收(释放不好会段错误,忘了释放会内存泄漏),于是就可以使得这个地址不会再被使用到,可以安全地返回。

## 如何进行逃逸分析?

在golang中,所有内存都是由runtime管理的,程序猿不需要关心具体变量分配在哪里,什么时候回收,但是编译器需要知道这一点,这样才能确定函数栈帧大小、哪些变量需要"new"在堆上,所以编译器需要进行`逃逸分析`。简单来说,`逃逸分析`决定了一个变量是分配在栈上还是分配在堆上。

golang逃逸分析最基本的原则是:`如果一个函数返回的是一个(局部)变量的地址,那么这个变量就发生逃逸`。

在golang里面,变量分配在何处和是否使用new无关,意味着程序猿无法手动指定某个变量必须分配在栈上或者堆上(自己撸asm的当我没说),所以我们需要通过一些方法来确定某个变量到底是分配在了栈上还是堆上。

我们用以下代码作为例子:

```go
package main

func main() {
    a := f1()
    *a++
}

//go:noinline
func f1() *int {
    i := 1
    return &i
}

上記のコードでは、F1のNOINLINEマークを高めるために、コンパイラ関数インライン化を行かせてはいけません。

コンパイラのパラメータを使用します

我々は、視覚的に変数のエスケープが唯一の構築行くときに指定する必要が発生したかどうかを見ることができるようにgolangは、パラメータのコンパイルを提供-gcflags '-m'します:

$ go build -gcflags '-m' escape.go
# command-line-arguments
./escape.go:3:6: can inline main
./escape.go:11:9: &i escapes to heap
./escape.go:10:2: moved to heap: i

このような視覚的に私は、発生した脱出ヒープメモリ上に確保され、ライン10,11を見ることができます。

外部のコンパイラパラメータを使用することに加えて、我々はまた、オブジェクトがエスケープするかどうかを判断するために、より低レベル、より多くのハードコア、より正確な方法を使用することができ、それは次のようになります。直接コンパイルしています!

アセンブラ

私たちは、使用go tool compile -S生成されたアセンブリコードを:

$ go tool compile -S escape.go | grep escape.go:10
    0x001d 00029 (escape.go:10) PCDATA  $2, $1
    0x001d 00029 (escape.go:10) PCDATA  $0, $0
    0x001d 00029 (escape.go:10) LEAQ    type.int(SB), AX
    0x0024 00036 (escape.go:10) PCDATA  $2, $0
    0x0024 00036 (escape.go:10) MOVQ    AX, (SP)
    0x0028 00040 (escape.go:10) CALL    runtime.newobject(SB)
    0x002d 00045 (escape.go:10) PCDATA  $2, $1
    0x002d 00045 (escape.go:10) MOVQ    8(SP), AX
    0x0032 00050 (escape.go:10) MOVQ    $1, (AX)

あなたは、00040コールがある見ることができるruntime.newobject(SB)この方法は、我々はこの方法が理解する必要があります参照してください!

概要

上記2つの方法が変換パラメータを用いて、可変エスケープが発生したかどうかを決定するために用いることができる提供することは、比較的単純な、比較的硬いコアアセンブラです。これらの2つの方法が脱出を解析したと、GCは圧力を緩和、さらにヒープ上のメモリの量を最適化することができるようになります。

おすすめ

転載: blog.51cto.com/14495811/2428533