スコープベースのメモリ管理の欠点


38

スコープベースのメモリ管理(SBMM)、またはRAIIは、C ++コミュニティでより一般的に(混乱して?)参照されるため、私は本当に気に入っています。私が知る限り、C ++(およびC)を除いて、SBMM / RAIIをメインメモリ管理メカニズムとする他の主流の言語は現在使用されておらず、代わりにガベージコレクション(GC)を使用しています。

これはかなりわかりにくい

  1. SBMMはプログラムをより決定的にします(オブジェクトがいつ破壊されるかを正確に知ることができます)。
  2. GCを使用する言語では、多くの場合、手動でリソース管理を行う必要があります(たとえば、Javaでファイルを閉じるを参照)。これは、GCの目的を部分的に無効にし、エラーも発生しやすくなります。
  3. ヒープメモリは(非常にエレガントに、imo)スコープにバインドすることもできます(std::shared_ptrC ++を参照)。

SBMMがより広く使用されないのはなぜですか?その短所は何ですか?


1
(特に速度に関する)いくつかの欠点がウィキペディアで議論されていますen.wikipedia.org/wiki/...
フィリップ・

2
Javaの手動のリソース管理の問題は、オブジェクトのfinalize()メソッドがガベージコレクションの前に呼び出されることを保証しないという副作用です。実際には、これにより、ガベージコレクションが解決するのと同じ問題のクラスが作成されます。
Blrfl 14年

7
@Blrflナンセンス。「問題」が存在しなくても、「メモリ」以外のリソースの「手動」リソース管理が望ましいでしょう。なぜなら、リソースが未使用になった後、またはまったく実行されなくてもGCは非常に長い時間実行されるからです。それはのため問題ありませんメモリ、およびメモリ管理は、ガベージコレクションが解決することになっているすべてのことです。

4
ところで。同じメカニズムを使用して、メモリだけでなく一般的なリソースを管理できるため、SBRMと呼びます。
PlasmaHH 14年

回答:


27

まず、メモリは他のすべてのリソースを組み合わせた場合よりもはるかに(数十、数百、または数千時間も)一般的であると仮定することから始めましょう。すべての単一の変数、オブジェクト、オブジェクトメンバには、それに割り当てられ、後で解放されるメモリが必要です。開くファイルごとに、数十から数百万のオブジェクトを作成して、ファイルから引き出したデータを保存します。すべてのTCPストリームは、ストリームに書き込まれるために作成された無制限の数の一時バイト文字列と一緒になります。同じページにいますか?すばらしいです。

RAIIが機能するためには(太陽の下ですべてのユースケースに既製のスマートポインターを使用している場合でも)、所有権を取得する必要があります。あなたは誰がいけない、と所有権がAからBに確かに転送する必要があるとき、あなたが共有所有権を使用することができ、これを所有しているか、またはそのオブジェクト必要があります誰が分析する必要があるすべてのものが、その後、あなたはスマートポインタ経由でGCをエミュレートすることと思います。その時点で、GCを言語に組み込むのがはるかに簡単かつ迅速になります。

ガベージコレクションは、最も一般的に使用されるリソースであるメモリに関するこの懸念から解放されます。もちろん、他のリソースについても同じ決定を行う必要がありますが、それらはあまり一般的ではなく(上記参照)、複雑な(共有など)所有権もあまり一般的ではありません。精神的負担が大幅に軽減されます。

ここで、すべての値をガベージコレクションするための欠点をいくつか挙げます。ただし、メモリセーフGC 値型の両方をRAIIと1つの言語に統合するのは非常に難しいので、他の方法でこれらのトレードオフを緩和する方が良いでしょうか?

確定性の喪失は、確定的なオブジェクトの有効期間のみに影響するため、実際にはそれほど悪くないことがわかります。次の段落で説明するように、ほとんどのリソース(メモリは別ですが、メモリは豊富で、かなり遅延してリサイクルできます)は、これらの言語のオブジェクトの有効期間にバインドされていません。他にもいくつかのユースケースがありますが、私の経験ではまれです。

2つ目のポイントである手動リソース管理は、スコープベースのクリーンアップを実行するステートメントで対処されていますが、このクリーンアップをオブジェクトの有効期間に結合しません(したがって、GCおよびメモリの安全性と相互作用しません)。これはusing、C#、withPython、およびtry最近のJavaバージョンの-with-resourcesにあります。


1
これは、GCのような非決定的モデルが決定的モデルより優れている理由を説明していません。 usingステートメントはローカルでのみ可能です。そのようにメンバー変数に保持されているリソースをクリーンアップすることは不可能です。
フィリップ

8
@Philippすべての共有所有権 GCであり、非常に貧弱なものです。参照カウントを暗示するために「共有所有権」をとる場合は、そう言ってください。しかし、amonの答えに対するコメントでサイクルについての議論を続けてください。また、OPが関心を持っているという意味でrefカウントが決定論的であるかどうかもわかりません(オブジェクトはできるだけ早く解放され、サイクルを割引しますが、多くの場合、プログラムを見ることでそれを知ることはできません)。さらに、すべての参照カウントは遅く、最新のトレースGCよりもはるかに遅くなります。

16
usingあなたが知っているように、RAIIと比較して冗談です。
DeadMG 14年

3
@フィリップは、「スーペリア」のメトリックを説明してください。手動メモリ管理は、メモリ管理を処理するために実行時に高速になることは事実です。ただし、ソフトウェアのコストは、メモリ管理のみに費やされるCPU時間だけで判断することはできません。
ArTs 14年

2
@ArTs:私はこれに必ずしも同意しません。RAIIには、オブジェクトがスコープを離れるときにオブジェクトを破棄する必要があるという要件があります。したがって、n個のオブジェクトを破棄するにはループが必要です。最新の世代別GCでは、これらの破棄はループの終わりまで、またはさらに後で延期され、1つの操作を実行するだけで何百もの反復に相当するメモリを破棄できます。良いケースでは、GC 非常に高速です。
Phoshi

14

RAIIは、Perlで使用されているように、自動参照カウントメモリ管理にも準拠しています。参照カウントは実装が簡単で、決定論的で、非常にパフォーマンスが高いですが、循環参照(リークの原因)を処理できないため、一般的に使用されていません。

ガベージコレクション言語 RAIIを直接使用できませんが、多くの場合、同等の効果を持つ構文を提供します。Javaでは、try-with-ressourceステートメントがあります

try (BufferedReader br = new BufferedReader(new FileReader(path))) { ... }

.close()ブロック終了時にリソースを自動的に呼び出します。C#にはIDisposableインターフェイスがあり.Dispose()using (...) { ... }ステートメントを離れるときに呼び出すことができます。Pythonには次のwithステートメントがあります。

with open(filename) as f:
    ...

同様に機能します。これに関する興味深いスピンで、Rubyのファイルを開くメソッドはコールバックを取得します。コールバックが実行された後、ファイルは閉じられます。

File.open(name, mode) do |f|
    ...
end

Node.jsも同じ戦略を使用していると思います。


4
リソース管理に高次関数を使用することは、Rubyよりもずっと前に遡ります。Lispsでは、たとえば、with-open-filehandleファイルを開いて関数に渡す関数を持ち、関数の戻り時にファイルを再び閉じることがよくあります。
ヨルグWミットタグ

4
循環参照引数は非常に一般的ですが、実際にはどのくらい重要ですか?所有権が明らかな場合、弱いポインターを使用して循環参照を緩和できます。
フィリップ

2
@Philipp参照カウントを使用する場合、所有権は通常明確ではありません。また、この回答では、参照カウントを排他的かつ自動的に使用する言語について説明しているため、弱い参照は存在しないか、強い参照よりも使用がはるかに困難です。

3
とにかく複雑なグラフで作業しているのでなければ、@ Philippの循環データ構造は非常にまれです。弱いポインターは、一般的な循環オブジェクトグラフでは役に立ちませんが、ツリー内の親ポインターのようなより一般的なケースでは役立ちます。回避策としては、グラフ全体への参照を表すコンテキストオブジェクトを保持し、破棄を管理することです。リカウントはディールブレーカーではありませんが、プログラマーがその制限を十分に認識している必要があります。つまり、GCよりも認知コストがわずかに高くなります。
アモン

1
参照カウントがめったに使用されない重要な理由は、その単純さにもかかわらず、GCよりも遅いことが多いことです。
ラッフルウィンド14年

14

私の意見では、ガベージコレクションの最も説得力のある利点は構成可能性考慮に入れることです。メモリ管理の正確さは、ガベージコレクション環境のローカルプロパティです。各部分を個別に見て、メモリリークの可能性があるかどうかを判断できます。メモリが正しいパーツをいくつでも組み合わせても、正しいままです。

参照カウントに依存すると、そのプロパティは失われます。アプリケーションがメモリをリークできるかどうかは、参照カウントによってアプリケーション全体のグローバルプロパティになります。パーツ間のすべての新しい相互作用は、間違った所有権を使用してメモリ管理を破る可能性があります。

異なる言語のプログラムの設計に非常に目に見える効果があります。GC言語のプログラムは、相互作用の多いオブジェクトのもう少しスープになる傾向がありますが、GCのない言語では、厳密に制御され制限された相互作用を持つ構造化パーツを好む傾向があります。


1
オブジェクトが参照を保持するのはオブジェクトのみであり、それらの参照のターゲットではない場合にのみ、正当性は構成可能です。通知のようなものがミックスに入ると(ボブは何かが起こったときに通知するようボブに頼み、ボブはそうすることを約束したのでボブはジョーへの参照を保持しますが、そうでなければボブはジョーを気にしません)、GCの正確さはしばしばスコープ付きリソース管理を必要とします[GCシステムにはC ++の自動化がないため、多くの場合手動で実装されます]。
supercat

@supercat:「GCの正確性には、多くの場合、スコープ付きリソース管理が必要です」。え?スコープはソースコードにのみ存在し、GCは実行時にのみ存在します(したがって、スコープの存在は完全に無視されます)。
ジョンハロップ

@JonHarrop:C ++の「スコープポインタ」と同じ意味で「スコープ」という用語を使用していました[オブジェクトのライフタイムは、それを保持するコンテナのライフタイムである必要があります]。私のポイントは、オブジェクトは、イベントの受信などの目的のために、純粋にGCシステムでは構成できない可能性があるため、潜在的に長命の参照を作成するということです。正確を期すために、特定の参照は強く、特定の参照は弱い必要があり、どの参照がオブジェクトの使用方法に依存するかが必要です。たとえば
...-supercat

...特定のディレクトリ内の何かが変更されるたびに、オブジェクトFredとBarneyが通知にサインアップするとします。Fredのハンドラーは、要求時にレポートできる値を持つが、他の用途がないカウンターをインクリメントするだけです。特定のファイルが変更されると、Barneyのハンドラーは新しいウィンドウをポップアップ表示します。正確さのために、フレッドは弱いイベントでサブスクライブする必要がありますが、バーニーは強いはずですが、タイマーオブジェクトにはそれを知る方法がありません。
-supercat

@supercat:そうです。私はそれが「しばしば」起こるとは言いません。プログラミングの30年に1度しか遭遇しませんでした。
ジョンハロップ

7

クロージャーは、ほとんどすべての現代言語の重要な機能です。それらはGCで実装するのが非常に簡単であり、RAIIで正しく動作するのは非常に難しい(不可能ではありませんが)です。

C ++は、他のすべての人が40年後にそれらを獲得しました。そして、それらを正しくするために、多くの賢い人々による多くのハードワークが必要でした。対照的に、多くのスクリプト言語には、プログラミング言語の設計と実装に関する知識がまったくない人々が設計および実装しています。


9
C ++のクロージャ良い例はないと思います。C ++ 11のラムダは、ファンクタクラス(C ++ 11よりかなり前の日付)の単なる構文上のシュガーであり、同様にメモリに対して安全ではありません。有効なよりも長い参照を保持するのと同じように、単にUBを取得します。彼らが40年遅れて登場したのは、FPを遅らせることによるものであり、FPを安全にする方法を考え出すためではありません。そして、それらを設計することは確かに大きな仕事でしたが、私はその努力の大部分が生涯の考慮に入れられたとは思いません。

私はデルナンに同意します:C ++はクロージャーを正しく取得しませんでした:呼び出し時にコアダンプを取得したくない場合は、非常に慎重にクロージャーをプログラムする必要があります。
ジョルジオ14年

2
@delnan:参照によるキャプチャラムダは非常に意図的にその[&]構文を持っています。すべてのC ++プログラマーは、すでに&記号を参照に関連付け、古い参照について知っています。
MSalters

2
@MSaltersあなたのポイントは何ですか?私は自分で参照接続を描きました。私は、C ++ラムダが例外的に安全でないとは言いませんでした。参照とまったく同じように安全でないと言いました。私はC ++のラムダが悪いと主張しませんでした、私はこの答えの主張に反対しました(C ++は正しい方法を見つけなければならなかったので、C ++は非常に遅くなった)。

5
  1. SBMMはプログラムをより決定的にします(オブジェクトがいつ破壊されるかを正確に知ることができます)。

ほとんどのプログラマにとって、OSは非決定的であり、メモリアロケータは非決定的であり、記述するプログラムのほとんどは並行しており、したがって本質的に非決定的です。大部分のプログラマーにとって、デストラクタがわずかに前または少し後ではなく、スコープの最後に呼び出されるという制約を追加することは、実用上の大きな利点ではありません。

  1. GCを使用する言語では、多くの場合、手動でリソース管理を行う必要があります(たとえば、Javaでファイルを閉じるを参照)。これは、GCの目的を部分的に無効にし、エラーも発生しやすくなります。

usingC#およびuseF#を参照してください。

  1. ヒープメモリは(非常にエレガントに、imo)スコープにバインドすることもできます(C ++のstd :: shared_ptrを参照)。

言い換えると、汎用ソリューションであるヒープを取得し、深刻に制限されている特定のケースでのみ動作するように変更することができます。もちろん、それは事実ですが、役に立ちません。

SBMMがより広く使用されないのはなぜですか?その短所は何ですか?

SBMMは、できることを制限します。

  1. SBMMは、ファーストクラスの字句クロージャーで上向きfunarg問題を作成します。これが、クロージャーがC#のような言語では一般的で使いやすいが、C ++ではまれで扱いにくい理由です。プログラミングでの関数構造の使用に向けた一般的な傾向があることに注意してください。

  2. SBMMはデストラクタを必要とし、関数が戻る前に実行する作業を追加することでテールコールを妨げます。テールコールは、拡張可能なステートマシンに役立ち、.NETなどによって提供されます。

  3. 一部のデータ構造とアルゴリズムは、SBMMを使用して実装するのが難しいことで有名です。基本的に、サイクルが自然に発生する場所であればどこでも可能です。最も顕著なのは、グラフアルゴリズムです。事実上、独自のGCを書くことになります。

  4. ここでは、制御フロー、したがってオブジェクトの有効期間が本質的に非決定的であるため、同時プログラミングはより困難です。メッセージパッシングシステムの実用的な解決策は、メッセージの深いコピーと過度に長い有効期間の使用です。

  5. SBMMは、ソースコードのスコープの終わりまでオブジェクトを存続させます。ソースコードは、必要以上に長くなることが多く、必要以上に長くなる可能性があります。これにより、浮遊ガーベッジ(リサイクルを待機している到達不能オブジェクト)の量が増加します。対照的に、ガベージコレクションのトレースは、オブジェクトへの最後の参照が消えた直後にオブジェクトを解放する傾向があります。メモリ管理の神話:promptnessを参照してください。

SBMMは非常に制限されているため、プログラマは、ライフタイムをネストすることができない場合にエスケープルートを必要とします。C ++ shared_ptrでは、エスケープルートを提供しますが、ガベージコレクションのトレースよりも10倍遅くなる可能性があります。したがって、GCの代わりにSBMMを使用すると、ほとんどの場合、ほとんどの人が足を間違えます。ただし、それが役に立たないということではありません。SBMMは、リソースが限られているシステムおよび組み込みプログラミングのコンテキストでは依然として価値があります。

FWIW ForthとAdaをチェックして、Nicolas Wirthの作品を読んでみてください。


1
どのビットを言ったら、私は記事を詳しく述べたり引用したりできるかもしれません。
ジョンハロップ

2
すべてのユースケースに偏在するのではなく、いくつかのまれなユースケースで10倍遅くなることはどの程度重要ですか?C ++にはunique_ptrがあり、ほとんどの目的には十分です。その次に、RAIIトラフC ++(古語であることが嫌いな言語)を攻撃するのではなく、言語を攻撃するRAIIトラフを攻撃する場合は、RAIIファミリーの若い兄弟(Rustなど)を試してください。Rustは基本的に、C ++が間違っていることをすべて正しくし、C ++が正しいことをほとんどのものを正しくしています。さらに「使用」すると、非常に限られた一連のユースケースが得られ、構成は無視されます。
user1703394

2
「すべてのユースケースに偏在するのではなく、いくつかのまれなユースケースで10倍遅くなるのはどの程度重要ですか?」まず、これは循環引数ですshared_ptr。C++では非常に遅いため、まれです。第二に、shared_ptr実稼働GCよりも何倍も遅いため、これはリンゴとオレンジの比較です(すでに引用した記事が示したように)。第三に、GCは遍在するものではなく、LMaxやRapid AdditionのFIXエンジンなどのソフトウェアでは回避されます。
ジョンハロップ

1
@ジョンハロップ、もしあなたが私を啓発してくれないなら。深いリソースを使用することによる推移的な影響を緩和するために、30年以上にわたってどのような魔法のレシピを使用しましたか?30年以上経ってもそのような魔法のレシピがなければ、私はあなたが他の原因に噛まれたと誤解されたに違いないと結論することができました。
user1703394

1
@Jon Harrop、shared_ptrは遅くありません。きちんと設計されたシステムでは「共有所有権」の必要性はまれであるため、shared_ptrは低速です。
user1703394

4

TIOBE(もちろん議論の余地はありますが、もちろんこれを使用しても問題ありません)のような人気インデックスを見ると、最初に上位20の〜50%が「スクリプト言語」または「SQL方言」であることがわかります。ここで、「使いやすさ」と抽象化の手段は、決定論的な動作よりもはるかに重要です。残りの「コンパイルされた」言語から、SBMMのある言語の約50%とない言語の約50%があります。したがって、計算からスクリプト言語を除外するとき、私はあなたの仮定が間違っていると思います。コンパイルされた言語の中で、SBMMを使用する言語は、使用しない言語と同じくらい人気があります。


1
「使いやすさ」は決定論とどう違うのですか?決定論的な言語は、非決定論的な言語よりも使いやすいと考えるべきではありませんか?
フィリップ

2
@Philipp決定論的であるか、実際に重要でないことのみ。オブジェクトの存続時間はそれ自体では重要ではありません(ただし、C ++と友人は、オブジェクトの存続時間に重要な多くのことを結び付けますが、可能だからです)。到達不能なオブジェクトが解放されるときは、定義上、それを使用しないため、問題ではありません。

もう1つ、PerlやPythonなどのさまざまな「スクリプト言語」でも、メモリ管理の主要な手段として参照カウントを使用しています。
フィリップ14年

1
@Philipp少なくともPythonの世界では、これはCPythonの実装の詳細であり、言語のプロパティではありません(他のすべての実装は参照カウントを避けています)。また、バックアップサイクルGCを使用したキャッチアウトなしの参照カウントは、SBMMまたはRAIIとしては適格ではないと主張します。実際、このスタイルのメモリ管理をRAIIに匹敵するものと考えるRAIIの支持者を見つけるのは難しいでしょう(ほとんどの場合そうではないので、どこでもサイクルすることでプログラムの他の場所での迅速な割り当て解除を防ぐことができます)。

3

誰も言及していないGCシステムの1つの大きな利点は、GCシステムの参照が存在する限りそのアイデンティティを保持することが保証されるということです。参照のコピーが存在する間にオブジェクトでIDisposable.Dispose(.NET)またはAutoCloseable.Close(Java)を呼び出すと、それらのコピーは同じオブジェクトを引き続き参照します。オブジェクトはもはや何の役にも立ちませんが、使用しようとすると、オブジェクト自体によって予測可能な動作が制御されます。対照的に、C ++では、コードがdeleteオブジェクトを呼び出して後で使用しようとすると、システムの状態全体が完全に未定義になります。

別の重要な注意点は、スコープベースのメモリ管理は、所有権が明確に定義されているオブジェクトに対して非常にうまく機能することです。所有権が定義されていないオブジェクトでは、あまりうまく機能せず、時にはひどくうまく機能しません。一般に、不変オブジェクトには所有者が必要ですが、不変オブジェクトには必要はありませんが、しわがあります。インスタンスを変更する可能性のあるコード。このようなシナリオでは、可変クラスのインスタンスは複数の不変オブジェクト間で共有される可能性があるため、明確な所有権はありません。


4
前半で参照するプロパティは、メモリの安全性です。GCはメモリの安全性を達成するための非常に簡単な方法ですが、GCは必要ありません。よくできた例についてはRustを見てください。

@delnan:rust-lang.orgを表示すると、ブラウザーはそこから便利に移動できないようです。詳細情報はどこで入手できますか?私の印象では、GCなしのメモリの安全性は、データ構造に特定の制限を課し、アプリケーションが行う必要のあるすべてのことにうまく適合しない可能性がありますが、間違っていることが証明されてうれしいです。
supercat

1
これに関する単一の(または少数の)良い参照も知りません。私のRustの知識は、メーリングリスト(およびさまざまなチュートリアル、言語設計ブログの投稿、githubの問題、ThisWeekInRustなどを含むメールにリンクされているすべてのもの)を読んで1、2年で蓄積されました。簡単にあなたの印象に対処するために:はい、各安全な構造は(必然的に)制限を課しますが、実質的にすべてのメモリ安全なコードに対して、適切な安全な構造が存在するか、書くことができます。群を抜いて最も一般的なものはすでに言語とstdlibに存在し、他のすべてはユーザーコードで記述できます。

@delnan:Rustは参照カウンターへのインターロック更新を必要としますか、それとも明確な所有権を持たない不変オブジェクト(または不変にラップされた可変オブジェクト)を処理する他の手段がありますか?Rustには「オブジェクト所有」と「非所有」の両方のポインターの概念がありますか?オブジェクトを「所有する」単一の参照を持つオブジェクトとそうでない参照の概念を説明した「Xonor」ポインターに関する論文を思い出します。「所有する」参照がスコープの外に出たとき、すべての非所有参照が...死んでオブジェクトへの参照になる、とのような特定できるだろう
supercat

1
Stack Exchangeのコメントは、言語を介してツアーを提供するのに適切な媒体ではないと思います。まだ興味がある場合は、ソース(#rust IRC、rust-devメーリングリストなど)に直接アクセスしたり、チャットルームを作成したりできます(作成できるはずです)。

-2

まず、RAIIをSBMMと同等にすることを理解することが非常に重要です。またはSBRMにさえ。RAIIの最も重要な(そしてあまり知られていない、または最も評価されていない)品質の1つは、「リソースであること」を構成に推移しないプロパティにするという事実です。

次のブログ投稿では、RAIIのこの重要な側面について説明し、非決定性GCを使用するGCed言語でのリソースの調整とは対照的です。

http://minorfs.wordpress.com/2011/04/29/why-garbage-collection-is-anti-productive/

RAIIは主にC ++で使用されますが、Python(最後に非VMベースのバージョン)にはデストラクタと確定的なGCがあり、RAIIをGCと一緒に使用できることに注意してください。両方の長所があれば最高です。


1
-1それは今まで読んだ最悪の記事の1つです。
ジョンハロップ

1
言語の問題は、GCをサポートすることではなく、RAIIを放棄することです。言語/フレームワークで両方をサポートできない理由はありません。
-supercat

1
@ジョンハロップ、詳しく教えてください。この記事で行われたクレームのうち、最初の3つのクレームのうち1つでも当てはまらないものはありますか?生産性の主張には同意できないかもしれませんが、他の3つの主張は絶対に有効です。最も重要なことは、リソースであるという推移性についての最初のものです。
user1703394

2
@ user1703394:第一に、記事全体は、実際にはガベージコレクションとは何の関係もないが、「GCed言語」のストローマンに基づいています。第二に、実際にはオブジェクト指向プログラミングに障害がある場合に、ガベージコレクションを非難します。最後に、彼の議論は10年遅すぎます。大部分のプログラマーは、生産性がはるかに高いため、ガベージコレクションされた言語にすでに近代化されています。
ジョンハロップ

1
彼の具体的な例(RAM、オープンファイルハンドル、ロック、スレッド)は非常にわかりやすいものです。これらのいずれかを直接処理するコードを作成しなければならなかった最後の時間を思い出すのは困難です。RAMを使用すると、GCはすべてを自動化します。ファイルハンドルを使用してFile.ReadLines file |> Seq.length、抽象化がクローズを処理するようなコードを記述します。.NET TaskとF#に置き換えたロックとスレッドMailboxProcessor。この「手作業によるリソース管理の量を爆発的に増やした」全体は、まったくのナンセンスです。
ジョンハロップ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.