13

言語を設計しています。まず、生成するコードを決定します。この言語には、レキシカル クロージャと、javascript に似たプロトタイプ ベースの継承があります。しかし、私は gc のファンではないので、できる限り避けようとしています。質問: スタック フレームをヒープに割り当ててガベージ コレクターに任せずにクロージャーを実装するエレガントな方法はありますか?

私の最初の考え:

  1. 参照カウントを使用し、サイクルをガベージ コレクションします (これはあまり好きではありません)。
  2. スパゲッティ スタックを使用します (非常に非効率に見えます)。
  3. クロージャの形成を一部のコンテキストに制限して、リターン アドレス スタックとローカル スタックを回避できるようにします。

高級言語を使用したり、呼び出し規約に従ったりしないので、好きなだけスタックを粉砕できます。

(編集:参照カウントがガベージコレクションの一種であることは知っていますが、より一般的な意味でgcを使用しています)

4

13 に答える 13

13

GC を使用しないことで回避しようとしていることを説明できれば、これはより良い質問です。ご承知のとおり、レキシカル クロージャを提供するほとんどの言語は、それらをヒープに割り当て、それらを作成したアクティベーション レコード内の変数バインディングへの参照を保持できるようにします。

私が知っているそのアプローチの唯一の代替手段gccは、ネストされた関数を使用するものです。関数のトランポリンを作成し、スタックに割り当てます。しかし、gcc のマニュアルには次のように書かれています。

含まれている関数が終了した後で、ネストされた関数をそのアドレスを介して呼び出そうとすると、すべての地獄が解き放たれます。含まれているスコープ レベルが終了した後で呼び出しを試み、スコープ内に存在しなくなった変数の一部を参照する場合は、幸運かもしれませんが、リスクを冒すのは賢明ではありません。ただし、ネストされた関数が範囲外のものを参照していない場合は安全です。

短いバージョンでは、次の 3 つの主な選択肢があります。

  • スタックにクロージャーを割り当て、含まれている関数が終了した後にそれらを使用できないようにします。
  • ヒープにクロージャーを割り当て、何らかのガベージ コレクションを使用します。
  • ML や Cyclone などが持っている地域のものから始めて、独自の研究を行います。
于 2008-09-18T02:00:22.540 に答える
9

このスレッドは役立つかもしれませんが、ここでの回答のいくつかはすでにそこにある回答を反映しています。

1枚のポスターが良い点です:

「真のガベージコレクションがない場合」にクロージャのガベージコレクションが必要なようです。consセルを実装するためにクロージャーを使用できることに注意してください。したがって、あなたの質問は「真のガベージコレクションがない場合の」ガベージコレクションに関するもののようです。関連する文献が豊富にあります。問題をクロージャに制限しても、実際には変更されません。

したがって、答えは次のとおりです。いいえ、クロージャを設定するエレガントな方法はなく、実際のGCもありません。最善の方法は、クロージャーを特定のタイプのクロージャーに制限するためのハッキングです。適切なGCがあれば、これはすべて不要です。

それで、私の質問はここにある他のいくつかの質問を反映しています-なぜGCを実装したくないのですか?単純なマーク+スイープまたはストップ+コピーには約2〜300行の(スキーム)コードが必要であり、プログラミング作業の点ではそれほど悪くはありません。プログラムを遅くするという点で:

  1. より優れたパフォーマンスを持つ、より複雑なGCを実装できます。
  2. あなたの言語のプログラムが苦しむことのないすべてのメモリリークについて考えてみてください。
  3. 利用可能なGCを使用したコーディングは祝福です。(C#、Java、Python、Perlなど...対C ++またはCを考えてください)。
于 2009-01-15T14:43:48.037 に答える
9

私は非常に遅れていることを理解していますが、偶然この質問に出くわしました。

クロージャーを完全にサポートするには実際に GC が必要だと思いますが、いくつかの特別なケースでは、スタック割り当てが安全です。これらの特殊なケースを判断するには、エスケープ分析が必要です。Closure Implementation in BitCなどのBitC 言語の論文を参照することをお勧めします。(論文が現在の計画を反映しているかどうかは疑問ですが。) BitC の設計者は、あなたと同じ問題を抱えていました。彼らは、エスケープする可能性のあるすべてのクロージャを拒否する、特別な非収集モードをコンパイラに実装することにしました。オンにすると、言語が大幅に制限されます。ただし、この機能はまだ実装されていません。

コレクターを使用することをお勧めします。これが最もエレガントな方法です。また、適切に構築されたガベージ コレクターは、malloc よりも高速にメモリを割り当てることも考慮する必要があります。BitC 関係者はパフォーマンスを重視しており、オペレーティング システム Coyotos のほとんどの部分でさえ GC で問題ないと考えています。簡単な方法で欠点を軽減できます。

  • 最小限のゴミしか作らない
  • プログラマーにコレクターを制御させる
  • エスケープ解析によるスタック/ヒープの使用の最適化
  • 増分コレクターまたは並行コレクターを使用する
  • 可能であれば、Erlang のようにヒープを分割します

多くの人は、Java の経験があるため、ガベージ コレクターを恐れています。Java には優れたコレクターがありますが、Java で作成されたアプリケーションには大量のガベージが生成されるため、パフォーマンスの問題があります。さらに、起動時間と応答時間が長くなるため、肥大化したランタイムと手の込んだ JIT コンパイルは、デスクトップ アプリケーションにはあまり適していません。

于 2009-04-26T03:14:59.280 に答える
4

参照カウントを使用し、サイクルをガベージコレクションします(私はこれが本当に好きではありません)

サイクルがないように言語を設計することは可能です。新しいオブジェクトのみを作成でき、古いオブジェクトを変更できない場合、およびオブジェクトを作成してもサイクルを作成できない場合、サイクルは表示されません。Erlangは基本的にこのように機能しますが、実際にはGCを使用します。

于 2008-10-11T10:06:24.360 に答える
4

C++ 0x 仕様では、ガベージ コレクションのないラムダが定義されています。つまり、仕様では、ラムダ クロージャに有効でなくなった参照が含まれている場合に、非決定論的な動作が許可されます。例 (疑似構文):

(int)=>int create_lambda(int a)
{
    return { (int x) => x + a }
}

create_lambda(5)(4)    // undefined result

この例のラムダはa、スタックに割り当てられた変数 ( ) を参照します。ただし、そのスタック フレームはポップされており、関数が戻ったときに必ずしも使用できるとは限りません。この場合、おそらく動作9し、結果として返されます (コンパイラのセマンティクスが正常であると仮定します) が、それを保証する方法はありません。

ガベージ コレクションを回避している場合は、明示的なヒープとスタックの割り当て、および (おそらく) ポインターも許可していると想定しています。その場合は、C++ のように行うことができます。その言語を使用する開発者は、ラムダの問題を特定し、明示的にヒープにコピーするのに十分スマートであると想定できます (内部で合成された値を返す場合と同様)。機能)。

于 2008-09-18T02:08:14.813 に答える
3

GC を正確にコピーするための機構がある場合は、最初にスタックに割り当て、ヒープにコピーして、終了時にこのスタック フレームへのポインターがエスケープされていることを発見した場合、ポインターを更新できます。そうすれば、このスタック フレームを含むクロージャを実際にキャプチャした場合にのみ料金が発生します。これが役立つか害があるかは、クロージャを使用する頻度とキャプチャする量によって異なります。

また、C++0x のアプローチ ( N1968 ) を調べることもできますが、C++ に期待されるように、プログラマーが何をコピーし、何を参照するかを指定する必要があり、間違った場合は無効なアクセスが発生するだけです。

于 2008-09-18T02:04:56.397 に答える
2

または、GC をまったく実行しないでください。メモリリークを忘れて、プロセスが完了したらプロセスをクリーンアップさせる方がよい場合があります。

GC に対する不満によっては、定期的な GC スイープが怖いかもしれません。この場合、アイテムが範囲外になったとき、またはポインターが変更されたときに、選択的 GC を実行できます。これがどれほど高価になるかはわかりません。

@アレン

含まれている関数が終了するときにクロージャーを使用できない場合、クロージャーは何の役に立つでしょうか? 私が理解していることから、それが閉鎖の要点です。

于 2008-09-18T02:05:42.370 に答える
2

すべてのクロージャーが最終的に正確に 1 回呼び出されるという前提で作業できます。これで、クロージャーが呼び出されたときに、クロージャーのリターンでクリーンアップを実行できます。

返されたオブジェクトをどのように処理する予定ですか? それらはある時点でクリーンアップする必要がありますが、これはクロージャーとまったく同じ問題です。

于 2009-01-15T14:17:30.277 に答える
1

遅くなるよりはましですか?

あなたはこれが面白いと思うかもしれません:差動実行

これはあまり知られていない制御構造であり、その主な用途は、使用中に動的に変更できるものを含む、ユーザーインターフェイスのプログラミングです。これは、Model-View-Controllerパラダイムの重要な代替手段です。

そのようなコードはクロージャとガベージコレクションに大きく依存すると思うかもしれないので、私はそれについて言及しますが、制御構造の副作用は、少なくともUIコードではそれらの両方を排除することです。

于 2010-06-01T18:44:04.460 に答える
0

MLの最後のバージョンはGCを控えめにしか使用していないことを読みました

于 2009-01-15T14:34:14.297 に答える
0

複数のスタックを作成しますか?

于 2008-10-21T13:39:31.193 に答える
0

プロセスが非常に短い場合、つまりメモリをあまり使用できない場合は、GC は不要だと思います。この状況は、スタック オーバーフローを心配することに似ています。入れ子を深くしすぎないでください。オーバーフローすることはありません。あまり長く実行しないでください。GC は必要ありません。クリーンアップは、事前に割り当てた大きな領域を再利用するだけの問題になります。より長いプロセスであっても、事前に割り当てられた独自のヒープを持つ小さなプロセスに分割できます。これは、たとえば、イベント ハンドラーでうまく機能します。コンパイラを作成している場合、うまく機能しません。その場合、GC は確かに大きなハンディキャップではありません。

于 2010-11-02T08:51:27.113 に答える