From c5d5955a9220f1deac03053d1ece7b6e36b71047 Mon Sep 17 00:00:00 2001 From: Ryusei Yamaguchi Date: Tue, 23 Feb 2016 04:25:17 +0900 Subject: [PATCH 1/6] [WIP] 5.1 The stack and the heap --- 1.6/ja/book/the-stack-and-the-heap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1.6/ja/book/the-stack-and-the-heap.md b/1.6/ja/book/the-stack-and-the-heap.md index 9cc3e12a..2610233a 100644 --- a/1.6/ja/book/the-stack-and-the-heap.md +++ b/1.6/ja/book/the-stack-and-the-heap.md @@ -1,4 +1,4 @@ -% The Stack and the Heap +% スタックとヒープ As a systems language, Rust operates at a low level. If you’re coming from a high-level language, there are some aspects of systems programming that you may From ceb4ed4aebc653326534782a76455d38d8e68fd3 Mon Sep 17 00:00:00 2001 From: Ryusei Yamaguchi Date: Tue, 23 Feb 2016 23:21:30 +0900 Subject: [PATCH 2/6] =?UTF-8?q?The=20Stack=20=E3=81=BE=E3=81=A7=E7=BF=BB?= =?UTF-8?q?=E8=A8=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1.6/ja/book/the-stack-and-the-heap.md | 130 +++++++++++++++++++++++++- TranslationTable.md | 3 + 2 files changed, 128 insertions(+), 5 deletions(-) diff --git a/1.6/ja/book/the-stack-and-the-heap.md b/1.6/ja/book/the-stack-and-the-heap.md index 2610233a..8a352114 100644 --- a/1.6/ja/book/the-stack-and-the-heap.md +++ b/1.6/ja/book/the-stack-and-the-heap.md @@ -1,12 +1,20 @@ % スタックとヒープ + +Rustはシステム言語なので、低水準の操作を行います。 +もしあなたが高水準言語を使ってきたのであれば、システムプログラミングのいくつかの側面をよく知らないかもしれません。 +一番重要なのは、スタックとヒープと関連してメモリがどのように機能するかということです。 +もしC言語のような言語でスタックアロケーションをどのように使っているかをよく知っているのであれば、この章は復習になるでしょう。 +そうでなければ、このより一般的な概念について、Rust流の焦点の絞り方ではありますが、学んでゆくことになるでしょう。 + + +ほとんどの物事と同様に、それらについて学ぶにあたって、まず簡略化したモデルを使って始めましょう。 +そうすることで、今は無関係な枝葉末節に足を取られることなく、基本を把握できます。 +これから使う例示は100%正確ではありませんが、現時点で学ぼうとするレベルのための見本になっています。 +ひとたび基本を飲み込めば、アロケータがどう実装されているかや仮想メモリなどの発展的なトピックを学ぶことによって、この特殊な抽象モデルが取り漏らしているものが明らかになるでしょう。 -# Memory management +# メモリ管理 + + +これら2つの用語はメモリ管理についてのものです。スタックとヒープは、いつメモリがアロケート・デアロケートするのかを決定するのを助ける抽象化です。 + +比較の概要です: + +スタックはとても高速で、Rustにおいてデフォルトでメモリが確保される場所です。 +しかし、このアロケーションはひとつの関数呼び出しに限られた局所的なもので、サイズに制限があります。 +一方、ヒープはより遅く、プログラムによって明示的にアロケートされます。 +しかし、事実上サイズに制限がなく、広域的にアクセス可能です。 -# The Stack +# スタック + + +次のRustプログラムについて話しましょう: ```rust fn main() { @@ -37,10 +67,17 @@ fn main() { } ``` + +このプログラムは変数 `x` の束縛をひとつ含んでいます。 +このメモリはどこかからアロケートされる必要があります。 +Rustはデフォルトで「スタックアロケート」、すなわち基本的な値を「スタックに置く」ということをします。 +それはどういう意味でしょうか。 + +関数が呼び出されたとき、関数中のローカル変数とそのほかの多少の情報のためにメモリがいくらかアロケートされます。 +これを「スタックフレーム」と呼びますが、このチュートリアルにおいては、余分な情報は無視して、アロケートするローカル変数だけを考えることにします。 +なので今回の場合は、 `main()` が実行されるとき、スタックフレームとして32ビット整数をただ1つアロケートすることになります。 +これは、見ての通り自動的に取り扱われるので、特別なRustコードか何かを書く必要はありません。 + +関数が終了するとき、スタックフレームはデアロケートされます。これもアロケーションと同様自動的に行われます。 + +これが、この単純なプログラムにあるものすべてです。 +ここで理解する鍵となるのは、スタックアロケーションはとても、とても高速だということです。 +ローカル変数はすべて事前にわかっているので、メモリを一度に確保できます。 +また、破棄するときも同様に、変数をすべて同時に破棄できるので、こちらもとても高速に済みます。 + +この話でよくないことは、単一の関数を超えて値が必要でも、その値を保持しつづけられないことです。 +また、「スタック」が何を意味するのかについてまだ話していませんでした。 +その点について見るために、もう少し複雑な例が必要です。 ```rust fn foo() { @@ -75,6 +131,7 @@ fn main() { } ``` + +このプログラムには変数が `foo()` に2つ、 `main()` に1つで、全部で3つあります。 +前の例と同様に `main()` が呼び出されたときは1つの整数がスタックフレームとしてアロケートされます。 +しかし、 `foo()` が呼び出されたときに何が起こるかを話す前に、まずメモリ上に何が置いてあるかを図示する必要があります。 +オペレーティングシステムは、メモリをプログラムに対してとてもシンプルなものとして見せています。それは、0からコンピュータが搭載しているRAMの容量を表現する大きな数までのアドレスの巨大なリストです。 +たとえば、もしあなたのコンピュータに1ギガバイトのRAMがのっていれば、アドレスは`0`から`1,073,741,823`になります。 +この数値は、1ギガバイトのバイト数である230から来ています。[^gigabyte] + + +[^gigabyte]: 「ギガバイト」が指すものには、 109 と 230 の2つがありえます。国際単位系 SI では「ギガバイト」は 109 を、「ギビバイト」は 230 を指すと決めることで、この問題を解決しています。しかしながら、このような用語法を使う人はとても少なく、文脈で両者を区別しています。ここでは、その慣習に則っています。 + +このメモリは巨大な配列のようなものです。すなわち、アドレスは0から始まり、最後の番号まで続いています。そして、これが最初のスタックフレームの図です: | Address | Name | Value | |---------|------|-------| | 0 | x | 42 | + +この図から、 `x` はアドレス `0` に置かれ、その値は `42` だとわかります。 + +`foo()` が呼び出されると、新しいスタックフレームがアロケートされます: | Address | Name | Value | |---------|------|-------| @@ -103,31 +179,53 @@ When `foo()` is called, a new stack frame is allocated: | 1 | y | 5 | | 0 | x | 42 | + +`0` は最初のフレームに取られているので、 `1` と `2` が `foo()` のスタックフレームのために使われます。 +これは、関数呼び出しが行われるたびに上に伸びていきます。 - + +ここで注意しなければならない重要なことがいくつかあります。 +`0`, `1`, `2` といった番号は単に解説するためのもので、コンピュータが実際に使うアドレス値とは関係がありません。 +特に、連続したアドレスは、実際にはそれぞれ数バイトずつ隔てられていて、その間隔は格納されている値のサイズより大きいこともあります。 + +`foo()` が終了した後、そのフレームはデアロケートされます: | Address | Name | Value | |---------|------|-------| | 0 | x | 42 | + +そして `main()` の後には、残っている値も消えてなくなります。簡単ですね! + +「スタック」という名は、積み重ねたディナープレート(a stack of dinner plates)のように働くことに由来します。 +最初に置かれたプレートは、最後に取り去られるプレートです。 +そのため、スタックはしばしば「last in, first out queues」(訳注: 最後に入ったものが最初に出るキュー、LIFOと略記される)と呼ばれ、最後にスタックに積んだ値は最初にスタックから取り出す値になります。 + +3段階の深さの例を見てみましょう: ```rust fn bar() { @@ -149,13 +247,19 @@ fn main() { } ``` + +いいですね、まず、 `main()` を呼び出します: | Address | Name | Value | |---------|------|-------| | 0 | x | 42 | + +次に、 `main()` は `foo()` を呼び出します: | Address | Name | Value | |---------|------|-------| @@ -164,7 +268,10 @@ Next up, `main()` calls `foo()`: | 1 | a | 5 | | 0 | x | 42 | + +そして `foo()` は `bar()` を呼び出します: | Address | Name | Value | |---------|------|-------| @@ -174,10 +281,16 @@ And then `foo()` calls `bar()`: | 1 | a | 5 | | 0 | x | 42 | + +ふう、スタックが高く伸びましたね。 + +`bar()` が終了した後、そのフレームはデアロケートされて `foo()` と `main()` だけが残ります: | Address | Name | Value | |---------|------|-------| @@ -186,14 +299,21 @@ After `bar()` is over, its frame is deallocated, leaving just `foo()` and | 1 | a | 5 | | 0 | x | 42 | + +そして `foo()` が終了すると `main()` だけが残ります: | Address | Name | Value | |---------|------|-------| | 0 | x | 42 | + +ついに、やりとげました。コツをつかみましたか? 皿を積み重ねるようなものです。 +つまり、一番上に追加し、一番上から取るんです。 # The Heap diff --git a/TranslationTable.md b/TranslationTable.md index 62a09e56..b7a390f5 100644 --- a/TranslationTable.md +++ b/TranslationTable.md @@ -14,6 +14,8 @@ | (lockの) acquire | 獲得 | aggregate type | 合成型 | alignment | アラインメント +| allocate | アロケートする +| allocation | アロケーション | allocator | アロケータ | antipattern | アンチパターン | application | アプリケーション @@ -48,6 +50,7 @@ | dangling | ダングリング | data race | データ競合 | deadlock | デッドロック +| deallocate | デアロケートする | declaration statement | 宣言文 | dereferencing | 参照外し | destructor | デストラクタ From 88e17365000cf3b11a72b226a8b7bbd0e5169a03 Mon Sep 17 00:00:00 2001 From: Ryusei Yamaguchi Date: Thu, 14 Apr 2016 15:35:31 +0900 Subject: [PATCH 3/6] =?UTF-8?q?The=20Heap=20=E3=81=BE=E3=81=A7=E7=BF=BB?= =?UTF-8?q?=E8=A8=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1.6/ja/book/the-stack-and-the-heap.md | 55 ++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/1.6/ja/book/the-stack-and-the-heap.md b/1.6/ja/book/the-stack-and-the-heap.md index 8a352114..64c6d8ad 100644 --- a/1.6/ja/book/the-stack-and-the-heap.md +++ b/1.6/ja/book/the-stack-and-the-heap.md @@ -250,7 +250,7 @@ fn main() { -いいですね、まず、 `main()` を呼び出します: +いいですか、まず、 `main()` を呼び出します: | Address | Name | Value | |---------|------|-------| @@ -315,14 +315,23 @@ add to the top, you take away from the top. ついに、やりとげました。コツをつかみましたか? 皿を積み重ねるようなものです。 つまり、一番上に追加し、一番上から取るんです。 -# The Heap +# ヒープ + + +さて、このやり方は結構うまくいくのですが、すべてがこのようにいくわけではありません。 +ときには、メモリを異なる関数間でやりとりしたり、1回の関数実行より長く保持する必要があります。 +そのためには、ヒープを使います。 + +Rustでは、[`Box` 型][box]を使うことで、メモリをヒープ上にアロケートできます。 ```rust fn main() { @@ -333,19 +342,31 @@ fn main() { [box]: ../std/boxed/index.html + +`main()` が呼び出されたとき、メモリは次のようになります: | Address | Name | Value | |---------|------|--------| | 1 | y | 42 | | 0 | x | ?????? | + +2つの変数のために、スタック上に領域がアロケートされます。 +通常通り、 `y` は `42` になりますが、 `x` はどうなるのでしょうか? +`x` は `Box` 型で、ボックスはヒープ上のメモリをアロケートします。 +このボックスの実際の値は、「ヒープ」へのポインタを持ったストラクチャです。 +関数の実行が開始され、 `Box::new()` が呼び出されると、ヒープ上のメモリがいくらかアロケートされ、そこに `5` が置かれます。 +すると、メモリはこんな感じになります: + | Address | Name | Value | |----------------------|------|------------------------| @@ -354,15 +375,22 @@ like this: | 1 | y | 42 | | 0 | x | → (230) - 1 | + +今考えている1GBのRAMを備えた仮想のコンピュータには (230) - 1 のアドレスがあります。 +また、スタックはゼロから伸びていますから、メモリをアロケートするのに一番都合が良い場所は、もう一方の端からです。 +ですから、最初の値はメモリのうち番号が一番大きい場所に置かれます。 +そして、 `x` にある構造体はヒープ上にアロケートした場所への[生ポインタ][rawpointer]を持っているので、 `x` の値は、今求めた (230) - 1 です。 [rawpointer]: raw-pointers.html + +ここまでの話では、メモリをアロケート・デアロケートするということのこの文脈における意味を過剰に語ることはありませんでした。 +詳細を深く掘り下げるのはこのチュートリアルの目的範囲外なのですが、ここで重要なこととして指摘したいのは、ヒープは単にメモリの反対側から伸びるスタックなのではないということです。 +後ほど例を見ていきますが、ヒープはアロケート・解放をどの順番にしてもよく、その結果「穴」のある状態になります。 +次の図は、とあるプログラムをしばらく実行していたときのメモリレイアウトです。 | Address | Name | Value | @@ -384,15 +417,22 @@ layout of a program which has been running for a while now: | 1 | y | 42 | | 0 | x | → (230) - 1 | + +この場合では、4つのものをヒープにアロケートしていますが、2つはすでにデアロケートされています。 +アドレス (230) - 1 と (230) - 4 の間には、現在使われていない隙間があります。このような隙間がなぜ、どのように起きるかの詳細は、どのようなヒープ管理戦略を使っているかによります。 +異なるブログラムには異なる「メモリアロケータ」というメモリを管理するライブラリを使うことができます。 +Rustのプログラムはこの用途に[jemalloc][jemalloc]を使います。 [jemalloc]: http://www.canonware.com/jemalloc/ + +ともかく、私たちのプログラムの例に戻ります。 +この(訳注: `x` のポインタが指す)メモリはヒープ上にあるので、ボックスをアロケートした関数よりも長い間メモリ上に留まることができます。 +しかし、この例ではそうではありません。[^moving] +関数が終了したとき、 `main()` のためのスタックフレームを解放する必要があります。 +しかし、`Box`には隠れた仕掛け、[Drop][drop]があります。 +`Drop`トレイトの`Box`への実装は、ボックスが作られたときにアロケートされたメモリをデアロケートします。すばらしい! +なので `x` が解放されるときには先にヒープ上にアロケートされたメモリを解放します。 | Address | Name | Value | |---------|------|--------| @@ -412,7 +460,10 @@ allocated on the heap: be covered later. + +その後スタックフレームが無くなることで、全てのメモリが開放されます。 # Arguments and borrowing From 7c4626565fdba17019561fdaba67a19c797da257 Mon Sep 17 00:00:00 2001 From: Ryusei Yamaguchi Date: Wed, 20 Apr 2016 01:17:01 +0900 Subject: [PATCH 4/6] =?UTF-8?q?The=20stack=20and=20the=20heap=20=E3=82=92?= =?UTF-8?q?=E6=9C=80=E5=BE=8C=E3=81=BE=E3=81=A7=E7=BF=BB=E8=A8=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1.6/ja/book/the-stack-and-the-heap.md | 135 ++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 10 deletions(-) diff --git a/1.6/ja/book/the-stack-and-the-heap.md b/1.6/ja/book/the-stack-and-the-heap.md index 64c6d8ad..b0a5b58f 100644 --- a/1.6/ja/book/the-stack-and-the-heap.md +++ b/1.6/ja/book/the-stack-and-the-heap.md @@ -384,9 +384,9 @@ allocated on the heap, so the value of `x` is (230) - 1, the memory location we’ve asked for. --> 今考えている1GBのRAMを備えた仮想のコンピュータには (230) - 1 のアドレスがあります。 -また、スタックはゼロから伸びていますから、メモリをアロケートするのに一番都合が良い場所は、もう一方の端からです。 +また、スタックはゼロから伸びていますから、メモリをアロケートするのに一番楽なのは、反対側の端の場所です。 ですから、最初の値はメモリのうち番号が一番大きい場所に置かれます。 -そして、 `x` にある構造体はヒープ上にアロケートした場所への[生ポインタ][rawpointer]を持っているので、 `x` の値は、今求めた (230) - 1 です。 +そして、 `x` にある構造体はヒープ上にアロケートした場所への[生ポインタ][rawpointer]を持っているので、 `x` の値は、今求めた位置 (230) - 1 です。 [rawpointer]: raw-pointers.html @@ -446,7 +446,7 @@ allocated on the heap: しかし、この例ではそうではありません。[^moving] 関数が終了したとき、 `main()` のためのスタックフレームを解放する必要があります。 しかし、`Box`には隠れた仕掛け、[Drop][drop]があります。 -`Drop`トレイトの`Box`への実装は、ボックスが作られたときにアロケートされたメモリをデアロケートします。すばらしい! +`Drop`トレイトの`Box`への実装は、ボックスが作られたときにアロケートされたメモリをデアロケートします。すばらしい! なので `x` が解放されるときには先にヒープ上にアロケートされたメモリを解放します。 | Address | Name | Value | @@ -463,12 +463,17 @@ allocated on the heap: -その後スタックフレームが無くなることで、全てのメモリが開放されます。 +その後スタックフレームが無くなることで、全てのメモリが解放されます。 -# Arguments and borrowing +# 引数と借用 + + +ここまででスタックとヒープの基本的な例をいくつか学び進めましたが、関数の引数と借用についてはどうでしょうか? +ここに小さなRustプログラムがあります: ```rust fn foo(i: &i32) { @@ -483,17 +488,26 @@ fn main() { } ``` + +処理が `main()` に入ると、メモリはこんな感じになります: | Address | Name | Value | |---------|------|--------| | 1 | y | → 0 | | 0 | x | 5 | + +`x` は普通の `5` で、 `y` は `x` への参照です。そのため、 `y` の値は `x` のメモリ上の位置で、今回は `0` です。 + +引数として `y` を渡している関数 `foo()` の呼び出しはどうなるのでしょうか? | Address | Name | Value | |---------|------|--------| @@ -502,18 +516,33 @@ What about when we call `foo()`, passing `y` as an argument? | 1 | y | → 0 | | 0 | x | 5 | + +スタックフレームは単にローカルな束縛のために使われるだけでなく、引数のためにも使われます。 +なので、この例では、引数の `i` とローカル変数の束縛 `z` の両方が必要です。 +`i` は引数 `y` のコピーです。 +`y` の値は `0` ですから、 `i` の値も `0` になります。 + +これは、変数を借用してもどのメモリもデアロケートされることがないことのひとつの理由になっています。 +つまり、参照の値はメモリ上の位置を示す単なるポインタです。 +もしポインタが指しているメモリを取り去ってしまうと、ことが立ちゆかなくなってしまうでしょう。 -# A complex example +# 複雑な例 + + +それでは、次の複雑な例をステップ・バイ・ステップでやっていきましょう: ```rust fn foo(x: &i32) { @@ -545,7 +574,10 @@ fn main() { } ``` + +まず、`main()`を呼び出します: | Address | Name | Value | |----------------------|------|------------------------| @@ -555,10 +587,16 @@ First, we call `main()`: | 1 | i | → (230) - 1 | | 0 | h | 3 | + +`j`, `i`, `h` のためのメモリをアロケートします。`i` (訳注: 正しくは `i` が束縛されるボックスが確保する領域)はヒープ上にあるので、 `i` はそこを指す値を持っています。 + +つぎに、 `main()` の最後で、 `foo()` が呼び出されます: | Address | Name | Value | |----------------------|------|------------------------| @@ -571,11 +609,19 @@ Next, at the end of `main()`, `foo()` gets called: | 1 | i | → (230) - 1 | | 0 | h | 3 | + +`x`, `y`, `z`のための空間が確保されます。 +引数 `x` は、渡された `j` と同じ値を持ちます。 +`j` は `h` を指しているので、値は `0` アドレスを指すポインタです。 + +つぎに、 `foo()` は `baz()` を呼び出し、 `z` を渡します: | Address | Name | Value | |----------------------|------|------------------------| @@ -590,8 +636,12 @@ Next, `foo()` calls `baz()`, passing `z`: | 1 | i | → (230) - 1 | | 0 | h | 3 | + +`f` と `g` のためにメモリを確保しました。 +`baz()` はとても短いので、 `baz()` の実行が終わったときに、そのスタックフレームを取り除きます。 | Address | Name | Value | |----------------------|------|------------------------| @@ -604,7 +654,10 @@ over, we get rid of its stack frame: | 1 | i | → (230) - 1 | | 0 | h | 3 | + +次に、 `foo()` は `bar()` を `x` と `z` を引数にして呼び出します: | Address | Name | Value | |----------------------|------|------------------------| @@ -623,11 +676,19 @@ Next, `foo()` calls `bar()` with `x` and `z`: | 1 | i | → (230) - 1 | | 0 | h | 3 | + +その結果、ヒープに値をもうひとつアロケートすることになるので、(230) - 1から1を引かなくてはなりません。 +そうすることは、単に `1,073,741,822` と書くよりは簡単です。 +いずれにせよ、いつものように変数を準備します。 + +`bar()` の最後で、 `baz()` を呼び出します: | Address | Name | Value | |----------------------|------|------------------------| @@ -648,10 +709,16 @@ At the end of `bar()`, it calls `baz()`: | 1 | i | → (230) - 1 | | 0 | h | 3 | + +こうして、一番深い所までやってきました! ふう! ここまで長い過程をたどってきて、お疲れ様でした。 + +`baz()` が終わったあとは、 `f` と `g` を取り除きます: | Address | Name | Value | |----------------------|------|------------------------| @@ -670,8 +737,12 @@ After `baz()` is over, we get rid of `f` and `g`: | 1 | i | → (230) - 1 | | 0 | h | 3 | + +次に、 `bar()` から戻ります。 +ここで `d` は `Box` 型なので、 `d` が指している (230) - 2 も一緒に解放されます。 | Address | Name | Value | |----------------------|------|------------------------| @@ -684,7 +755,10 @@ what it points to: (230) - 2. | 1 | i | → (230) - 1 | | 0 | h | 3 | + +その後、 `foo()` から戻ります: | Address | Name | Value | |----------------------|------|------------------------| @@ -694,46 +768,79 @@ And after that, `foo()` returns: | 1 | i | → (230) - 1 | | 0 | h | 3 | + +そして最後に `main()` から戻るところで、残っているものを除去します。 +`i` が `Drop` されるとき、ヒープの最後の残りも除去されます。 -# What do other languages do? +# 他の言語では何をしているのか? + + +ガベージコレクタを備えた多くの言語はデフォルトでヒープアロケートします。 +つまり、すべての値がボックス化されています。 +そうなっている理由がいくつかあるのですが、それはこのチュートリアルの範囲外です。 +また、そのことが100%真であると言えなくなるような最適化もいくつか行われることがあります。 +メモリの解放のためにスタックと `Drop` を頼りにするかわりに、ガベージコレクタがヒープを取り扱います。 -# Which to use? +# どちらを使えばいいのか? + + +スタックのほうが速くて管理しやすいというのであれば、なぜヒープが要るのでしょうか? +大きな理由のひとつは、スタックアロケーションだけしかないということはストレージの再利用にLIFOセマンティクスをとるしかないということだからです。 +ヒープアロケーションは厳密により普遍的で、ストレージを任意の順番でプールから取得したり、プールに返却することが許されているのですが、よりコストがかさみます。 + +一般的にはスタックアロケーションを選ぶべきで、そのためRustはデフォルトでスタックアロケートします。 +スタックのLIFOモデルはより単純で、基本的なレベルに置かれています。 +このことは、実行時の効率性と意味論に大きな影響を与えています。 -## Runtime Efficiency +## 実行時の効率性 + + +スタックのメモリを管理するのは些細なことです: 機械は「スタックポインタ」と呼ばれる単一の値を増減するだけです。 +ヒープのメモリを管理するのは些細なことではありません: ヒープアロケートされたメモリは任意の時点で解放され、またヒープアロケートされたそれぞれのブロックは任意のサイズになりうるので、一般的にメモリマネージャは再利用するメモリを特定するためにより多く働きます。 + +この事柄についてより詳しいことを知りたいのであれば、[こちらの論文][wilson]がよいイントロダクションになっています。 [wilson]: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.143.4688 -## Semantic impact +## 意味論への影響 + + +スタックアロケーションはRustの言語自体へ影響を与えており、したがって開発者のメンタルモデルにも影響しています。 +Rust言語がどのように自動メモリ管理を取り扱うかは、LIFOセマンティクスに従っています。 +ヒープアロケートされユニークに所有されたボックスのデアロケーションさえも、スタックベースのLIFOセマンティクスに従っていることは、この章を通して論じてきたとおりです。 +非LIFOセマンティクスの柔軟性(すなわち表現能力)は一般的に、いつメモリが解放されるべきなのかをコンパイラがコンパイル時に自動的に推論できなくなることを意味するので、デアロケーションを制御するために、ときに言語自体の外部に由来するかもしれない、動的なプロトコルに頼らなければなりません。(`Rc` や `Arc` が使っている参照カウントはその一例です。) + +突き詰めれば、ヒープアロケーションによって増大した表現能力は(例えばガベージコレクタという形の)著しい実行時サポートか、(Rustコンパイラが提供していないような検証を必要とする明示的なメモリ管理呼び出しという形の)著しいプログラマの努力のいずれかのコストを引き起こすのです。 From a6707e474e28abc55688cf72e844132b7b63d776 Mon Sep 17 00:00:00 2001 From: Ryusei Yamaguchi Date: Wed, 20 Apr 2016 01:18:21 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=E7=BF=BB=E8=A8=B3=E8=A1=A8=E3=81=AB?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TranslationTable.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TranslationTable.md b/TranslationTable.md index b7a390f5..1de457ea 100644 --- a/TranslationTable.md +++ b/TranslationTable.md @@ -30,6 +30,7 @@ | block | ブロック | borrowing | 借用 | bounds | 境界 +| boxed | ボックス化された | bug | バグ | capture | キャプチャ | case analysis | 場合分け @@ -76,7 +77,9 @@ | exterior | 外側の | feature | フィーチャ | foreign | 他言語 +| free | 解放する | free-standing function | フリースタンディングな関数 +| garbage collector | ガベージコレクタ | generic parameter | ジェネリックパラメータ | generics | ジェネリクス | glob | グロブ @@ -136,6 +139,7 @@ | raw pointer | 生ポインタ | re-assignment | 再代入 | rebind | 再束縛 +| reference | 参照 | reference count | 参照カウント | regression | リグレッション | release | リリース From ce44ee4b6ee38477b68ae8e5ab739dd0829f311b Mon Sep 17 00:00:00 2001 From: Ryusei Yamaguchi Date: Thu, 21 Apr 2016 00:26:21 +0900 Subject: [PATCH 6/6] =?UTF-8?q?The=20stack=20and=20the=20heap:=20=E8=A8=B3?= =?UTF-8?q?=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1.6/ja/book/the-stack-and-the-heap.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/1.6/ja/book/the-stack-and-the-heap.md b/1.6/ja/book/the-stack-and-the-heap.md index b0a5b58f..780666d7 100644 --- a/1.6/ja/book/the-stack-and-the-heap.md +++ b/1.6/ja/book/the-stack-and-the-heap.md @@ -35,12 +35,12 @@ this particular abstraction. These two terms are about memory management. The stack and the heap are abstractions that help you determine when to allocate and deallocate memory. --> -これら2つの用語はメモリ管理についてのものです。スタックとヒープは、いつメモリがアロケート・デアロケートするのかを決定するのを助ける抽象化です。 +これら2つの用語はメモリ管理についてのものです。スタックとヒープは、いつメモリをアロケート・デアロケートするのかを決定するのを助ける抽象化です。 -比較の概要です: +大まかに比較してみましょう: ここまでの話では、メモリをアロケート・デアロケートするということのこの文脈における意味を過剰に語ることはありませんでした。 詳細を深く掘り下げるのはこのチュートリアルの目的範囲外なのですが、ここで重要なこととして指摘したいのは、ヒープは単にメモリの反対側から伸びるスタックなのではないということです。 -後ほど例を見ていきますが、ヒープはアロケート・解放をどの順番にしてもよく、その結果「穴」のある状態になります。 +後ほど例を見ていきますが、ヒープはアロケート・デアロケートをどの順番にしてもよく、その結果「穴」のある状態になります。 次の図は、とあるプログラムをしばらく実行していたときのメモリレイアウトです。 @@ -442,7 +442,7 @@ when it was created. Great! So when `x` goes away, it first frees the memory allocated on the heap: --> ともかく、私たちのプログラムの例に戻ります。 -この(訳注: `x` のポインタが指す)メモリはヒープ上にあるので、ボックスをアロケートした関数よりも長い間メモリ上に留まることができます。 +この(訳注: `x` のポインタが指す)メモリはヒープ上にあるので、ボックスをアロケートした関数よりも長い間生存しつづけることができます。 しかし、この例ではそうではありません。[^moving] 関数が終了したとき、 `main()` のためのスタックフレームを解放する必要があります。 しかし、`Box`には隠れた仕掛け、[Drop][drop]があります。 @@ -455,10 +455,14 @@ allocated on the heap: | 0 | x | ?????? | [drop]: drop.html + +[^moving]: (「変数からのムーブアウト」とも呼ばれることもある)所有権の移動によって、メモリをより長い間生存させられます。 + + より複雑な例は後ほど解説します。 -`j`, `i`, `h` のためのメモリをアロケートします。`i` (訳注: 正しくは `i` が束縛されるボックスが確保する領域)はヒープ上にあるので、 `i` はそこを指す値を持っています。 +`j`, `i`, `h` のためのメモリをアロケートします。`i` が束縛されるボックスが確保する領域はヒープ上にあるので、 `i` はそこを指す値を持っています。