前回はRust言語のヒープ管理が「クセが強い」みたいな事を書いてしまいました。「クセが強い」ことでは、Go言語のヒープ管理も負けてない、と思います(個人の感想です。)「ヒープ・エスケープ」という技?を最初みたときには、これは何?と結構驚きました。伝統的なC言語ではやっちゃいけない(やったらひどいことになる)ことがOK。
※「やっつけな日常」投稿順 indexはこちら
※今回はWSL1上のUbuntu20.04LTSにインストールした以下のバージョンのGoで確認してます。
ヒープ・エスケープ(heap escape)
GoもRustも、ヒープの管理は、Cのようなプログラマ管理のalloc/freeではなくモダーンな感じに自動です。しかしその開放の仕方は異なっていて、今回やってみるGoはギャベージ・コレクタ(GC)を使っています。
ヒープをアサインする方法も独特です。Go言語の場合、ヒープに置くべきだとコンパイラが判断したメモリオブジェクト(変数含む)は勝手にヒープに置かれます。もちろんスタックに置いても大丈夫なものはスタックに置かれます。C言語ではstaticでないローカル変数が必ずスタックの上にとられるのとは大違いです。Goのこの仕組みをヒープ・エスケープと言うそうです。スタックから逃げるってことなのかい?知らんけど。
以下は単純変数を殊更にヒープ・エスケープするように書いてみた例です。tst1関数はヒープ・エスケープされ、tst2関数はされません。
目論見どおり動作することを確認するため実行してみたところが以下に。問題なく実行されているみたいです。
ううむ、Cでtst1みたいなこと「関数内部で宣言したローカル変数へのポインタを該当の関数を抜けてから使う」をすると、たまたま動いているように見える(まだスタックが再利用されていない場合)こともあるかと思いますが、まったく関係ない変数にアクセスしてしまったり、システムによってはメモリ保護に引っかかってクラッシュするようなことも考えられます。ヤバいやつ。
Go言語のやり方は確かに便利で、慣れるとハマりそうな気もするのです(Goでコード書くときには毎度お世話になってます。)でもGoの外でついやってしまいそうで心配、恐ろしいデス。Goに入ればGoに従えと。しつこいな。