【Lua学習記】Lua Advanced - ガベージコレクション

ここに画像の説明を挿入します
Tang 先生のコースによると、組み込みライブラリについて教えることになっていましたが、考えてみてください。ドキュメントを読むこともできますし、Ctrl + 左クリックで注釈を読むこともできます。そして最も重要なことは、組み込みライブラリのメソッドの多くは、のライブラリはほとんどの言語で利用できます。実際に見てみると理解できるでしょう。それでは、ガベージ コレクションに焦点を当ててみましょう。

以下の内容の大部分は、Lua】ガベージコレクションの詳細解説Luaのソースコード解析 - gc実装の仕組み【詳細版】 (1)から抜粋したものですので、リンク先の記事をお読みください。


GC

Unity から Lua に触れる場合は、C# にも GC メカニズムがあることを知っておく必要があります。仮想マシンはほとんどのオブジェクト指向言語で使用され、Mono 仮想マシンも Unity で使用されます。私たちのコードは仮想マシンの仮想メモリ内にあります。仮想マシンでは、コードは命令セットに変換され、バイトコードにコンパイルされ、最終的にバイトコードに変換された命令セットが各プラットフォームで使用されてクロスプラットフォーム実装が実現されます。

オブジェクトが占めるメモリ空間は仮想メモリに保存されるため、物理メモリには影響しません。物理メモリがオブジェクトを呼び出す必要がある場合、仮想メモリ ブロックが物理メモリに割り当てられます。仮想メモリ ブロックが物理メモリから長期間参照されない場合、仮想メモリ ブロックが占有しているメモリを解放する必要があります。これが GC (ガレージ コレクト) です。

公式説明によると
、Lua は GC メカニズムを通じてメモリを自動的に管理し、文字列、テーブル、ユーザーデータ、関数、スレッド、内部構造などの無効なオブジェクトを削除できます。

死んだとみなされているオブジェクトはプログラム内でアクセスできなくなります (ただし、ファイナライザーはこれらの死んだオブジェクトを復活させることができます)。GC が死んだものと考えるのは、プログラマが考えるものとは異なります。GC は、オブジェクトが長期間非アクティブであると、そのオブジェクトが死んでいると判断します。オブジェクトが死んでいるとみなされると、そのオブジェクトは正常にアクセスできなくなります。

関数を使用するcollectgarbageか、メタメソッドを定義することにより__gc、gc メカニズムを直接使用またはオーバーライドできます。


補助ガベージコレクション

自動ガベージ コレクションはほとんどの場合に適用できますが、特殊なケースでは、ガベージ コレクションのオブジェクトとタイミングを自分で決定する必要がある場合もあります。この目的のために、Lua 言語はガベージ コレクションを支援する方法を提供します。

  • collectgarbage 関数: ガベージ コレクターのステップ サイズを制御できます。
  • デストラクター(最近、C# の GC エントリーを確認してください。Microsoft の正式な中国語名は、ファイナライザーと呼ばれています) (ファイナライザー): ガベージ コレクターの直接制御下にない外部オブジェクトのコレクションを許可します。
  • 弱参照テーブル (弱いテーブル): プログラムからもアクセスできる Lua のオブジェクトのコレクションを許可します (特にテーブル内の null キー値)

ゴミを集める

---@alias gcoptions
---|>"collect"      # 做一次完整的垃圾收集循环。
---| "stop"         # 停止垃圾收集器的运行。
---| "restart"      # 重启垃圾收集器的自动运行。
---| "count"        # 以 K 字节数为单位返回 Lua 使用的总内存数。
---| "step"         # 单步运行垃圾收集器。 步长“大小”由 `arg` 控制。
---| "isrunning"    # 返回表示收集器是否在工作的布尔值。
---| "incremental"  # 改变收集器模式为增量模式。
---| "generational" # 改变收集器模式为分代模式。

---
---这个函数是垃圾收集器的通用接口。 通过参数 opt 它提供了一组不同的功能。
---
function collectgarbage(opt, ...) end

// 使用方法collectgarbage("加内关键字")
collectgarbage("collect")  --主动进行一次垃圾回收(垃圾回收会回收nil的垃圾)

ガベージ コレクションは毎回大量のメモリを消費するため、使用はできるだけ少なくし、手動で実行できる場合は自動的に使用しないでください。

collectgarbageインクリメンタル モードとジェネレーション モードという 2 つのリサイクル モードが提供されます。

インクリメンタルモード

インクリメンタル モードでは、各 gc サイクルはマーク アンド スイープ方式を使用して、ガベージを徐々にマークして収集します。GC とインタプリタは交互に一緒に実行されます (Lua5.1 以降では、メイン プログラムの実行を停止する必要はありません)。インタプリタが一定量のメモリを割り当てると、ガベージ コレクタもステップを実行します。各 GC サイクルは、マーク、クリーニング、スイープ、ファイナライズの 4 つのフェーズで構成されます。

collectgarbage("incremental",200,200,13)
1)、garbage-collector pause, 
    什么时间执行,比上次回收后内增加的比例 默认200% 最大1000%
2)、garbage-collector step multiplier, 
相对说内存分配而言的一个比例,也就是是以什么速度收集 默认 200% 最大 1000%
3)、 the garbage-collector step size
控制每次回收的步幅?解释器分配的字节数 默认是 213次 约 8K

以下の内容の大部分は、Lua ソースコード解析 - gc 実装の仕組み【詳細版】(1)より抜粋したものです。

collectgarbage上記のパラメータは、インクリメンタル モードの一部のパラメータを制御します。インクリメンタル モードでは、最初にマーキングしてから処理することを意味するマーク アンド スイープ方式が使用されます。簡単に言えば、3 色マーキング方式は、すべてのオブジェクトをツリー構造に配置するために使用されます、親子関係はリンク リストとヘッド挿入メソッドを使用して要素を追加できます。
ここに画像の説明を挿入します
ここに画像の説明を挿入します

初期状態ではノードの色はすべて白です。白は参照がないことを表します
ここに画像の説明を挿入します
。これで、参照関係は上の図のようになります。次に、ルート ノードから開始して参照関係をたどって、リンク リスト内のノードに色を付けます。ノード 1 には参照があるため、灰色に色を付け、その参照関係を挿入しここに画像の説明を挿入します
ますリンク リストをグレーにして続行します。
ここに画像の説明を挿入します
子 No. 1 には No. 4 への参照があります。No. 1 を黒に色付けしてリンク リストに追加します。次に、No. 4 をグレーに色付けしてリンク リストに追加します。

以降、上記の手順を繰り返すと、No.4 が黒色でリンクリストから外れ、789 がグレーでリンクリストに入ります。灰色のリンク リストにノードがなくなるまで順番に実行すると、参照されているすべてのノードが黒になります。

最後のステップはクリーンアップ プロセスです。スイープ ノードは rootgc リンク リストを順番に走査し、すべての白いノードが提案されます。クリアする前に白いノードが突然参照された場合、そのノードは保護色である白で着色され、削除されないこと。終了後、次のクリーニングを容易にするために、すべての黒いノードを白に設定します。

世代モデル

collectgarbage("generational",20,200)
1,minor
    比例数,相对于上次major回收增长的比例,达到即执行minor , 默认20% 最大200%
2), major 
    比例数,内存使用增长比例达到就执行回收,默认100,最大1000

上記のパラメーターはcollectgarbage、反復モードの一部のパラメーターを制御します。世代別 GC モードでは、ガベージ コレクターは、すべてのオブジェクトをスキャンするのではなく、毎回最後に作成されたオブジェクトからスキャンして、オブジェクト内のガベージをクリーンアップする小さなガベージ コレクションを頻繁に実行します。このような小規模な GC の後でもメモリが制限を超える場合は、プログラムの実行が一時停止され、GC のためにすべてのオブジェクトが走査されます。


__gc

メタテーブルは gc のメタメソッドも提供します__gc。メタメソッドで定義された関数は正式にはファイナライザーと呼ばれます。当面は Zhihu では「デストラクター」と呼ばれています(最近 Microsoft によって正式に採用された C# の GC エントリを確認してください)はターミネータです)。メタメソッドが定義されたこのオブジェクトが gc によってリサイクルされると、ファイナライザーで関数が実行されます。このメタメソッドを使用すると、特定のオブジェクトがクリーンアップされるときにファイナライザー関数を呼び出したり、特定のオブジェクトを復活させてクリーンアップされないようにすることができます。

例 1:

t = {
    
    name = "zhangsan"}
setmetatable(t,{
    
    __gc = function (t)
    print(t.name)
end})
t = nil
--调用t的析构函数,打印zhangsan

例 1 では、zhangsan は出力されますが、 t は gc によってクリーンアップされます。実際のプロセスは次のとおりです: gc はオブジェクトのクリーンアップを開始します -> ファイナライザーを使用し、t.name を出力します (t=nil ですが、ファイナライザーによって一時的に復活しました) 、ターミネーター実行後にまた死ぬ)->gc cleanup

メタテーブルの設定時にオブジェクトが __gc メタメソッドを追加せず、メタテーブルの作成後に追加した場合、オブジェクトはリサイクル時に __gc メタメソッドをトリガーできません。

t = {
    
    name = "zhangsan"}
mt = {
    
    }
setmetatable(t,mt)
--先设置元表,再为元表添加__gc元方法
mt.__gc = function (t)
    print(t.name)
end
t = nil
--不会输出任何值(未执行终结器)

ファイナライザーはリサイクルされたオブジェクトにアクセスする必要があるため、Lua はオブジェクトを復活させる必要があります。通常、この復活は短期間であり、このオブジェクトによって占有されていたメモリは次の GC 中に解放されます。ただし、ファイナライザーがオブジェクトをグローバルな場所 (グローバル変数など) に保存する場合、この復活は永続的になります。

t = {
    
    name = "zhangsan"}
setmetatable(t,{
    
    __gc = function (t)
    print(t.name)
    a = t   --在析构函数中将它赋值给全局变量
end})
t = nil
collectgarbage()    --在此处要手动垃圾回收,否则由于下方还有语句不会执行gc,而a也就不会被赋值了,打印zhangsan
print(a.name)       --t引用的对象并未被回收(永久复活),打印zhangsan

弱いテーブル 弱い参照テーブル

いくつかのアクティブなオブジェクトを保存したい場合はどうすればよいでしょうか? それを配列に入れるだけですが、オブジェクトが配列に追加されると、それは再利用できなくなります。それは、オブジェクトが他の場所で参照されていなくても、依然として配列に含まれているからです。ただし、**弱いテーブル** を通じて、この配列内の参照がこのオブジェクトのリサイクルに影響を与えてはならないことを Lua に明示的に伝えることができます。

弱参照は、ガベージ コレクターによって考慮されない参照を指します。オブジェクトへのすべての参照が弱参照である場合、ガベージ コレクターはオブジェクトをリサイクルし、これらの弱参照を削除します。弱参照テーブルは、Lua 弱参照メソッドで実装されます。

テーブルが弱い参照テーブルであるかどうかは、メタテーブルの__modeフィールドによって決まります。次の 3 つの状況があります。

  • キーの弱参照:__mode = "k"ガベージ コレクターがキーを再利用できるように設定しますが、値は再利用できません。
  • 値の弱参照: を設定すると__mode = "v"、ガベージ コレクターはその値を再利用できますが、キーは再利用できません。エフェメロンテーブルとも呼ばれ、キーがアクセス可能な場合にのみ値にアクセスできます。キーにアクセスできない場合、値もガベージ コレクターによってテーブルから削除されるためです。
  • キーと値はすべて弱参照です。 を設定することで__mode = "kv"、キーと値の再利用が許可されます。

tableいずれの場合でも、キーまたは値がリサイクルされるtableすぐに、キーと値のペア全体がそこから削除されることを強調しておく必要があります。

以下の内容はLua 基本の弱参照からの抜粋です

Lua は自動ガベージ コレクションを備えたメモリ管理メカニズムを使用しますが、Lua はオブジェクトを破棄する必要があるかどうかを正しく判断できない場合があり、破棄する必要があるオブジェクトが常に存在し、メモリ リークが発生することがあります。

a = {
    
    }
key = {
    
    }
print(a[key])
a[key] = 1
print(a[key])
key = {
    
    }
print(a[key])
a[key] = 2
collectgarbage()
for k,v in pairs(a) do
    print(k, v)
end
输出:
nil
1
nil
table: 00000000006da000	1
table: 00000000006da380	2

key={} 以降の a[key] は nil でしたが、トラバースすると 1 が得られ、破壊されていないことがわかります。これは、この a[key] は配列に格納されており、配列内のキーと値のペアは空であっても GC によって削除することができず、この状況によりメモリ リークが発生するためです。したがって、この状況を回避するために、弱い参照を使用して GC メカニズムに指示します。配列ではありますが、その中の空のキー値は削除できます。

a = {
    
    }
b = {
    
    __mode = "k"}
setmetatable(a,b)
key = {
    
    }
a[key] = 1
key = {
    
    }
a[key] = 2
collectgarbage()
for k,v in pairs(a) do
    print(v)
end
输出:
2

おすすめ

転載: blog.csdn.net/milu_ELK/article/details/131984125