Windowsでシステムプログラミングを行っている場合、 Heap32First / Heap32Nextなどの同じファミリの非常に便利な機能が、Windows 7から非常に遅く動作し始めたことに気付くかもしれません。
遠い1992年に早送りしましょう。 Windows 3.1は開発中です。 その中の新しいコンポーネントの1つはToolHelpです。 彼は、OSカーネルの内部を少し掘り下げることを許可しました。 私たちにとって最も興味深い関数は、ヒープ(ヒープ)のデータを表示できるようにする関数です。 Windows 3.1は同様の機能を呼び出すことで協調マルチタスクを使用したため、ヒープの内容がHeapFirstとHeapNextの呼び出し間で変わらないことを確認できました。OSにはプロセスを中断してコンテキストを別のコンテキストに切り替える権限がないためです。 時がありました!
ToolHelpはOSカーネルのコンポーネントではないことに注意してください。 彼らは横にそれをねじ込みました。 その理由は、ToolHelpの開発は既にWindows 3.1のリリースに近づいており、カーネル開発チームはこのような後期段階でそれを不安定にしたくないからです。 したがって、ToolHelpが行うことはすべて「外部」であり、これにはいくつかの制限があります。
Widows 95は、同じ機能の32ビットバージョンをToolHelpに追加しました。
だから私は何を話している...ああそう、Heap32Next。
ToolHelpの32ビットバージョンでは、Heap32FirstおよびHeap32Next関数を呼び出すことにより、ヒープ上のデータを表示できます。 Windows 95でのこれらの関数の実装は次のように機能しました。Heap32First呼び出しは特定の量のメモリを割り当て、そのサイズをHEAPENTRY32.dwResvdフィールドに保存しました。 その後のHeap32Nextの呼び出しでは、この値を使用して次のメモリブロックを読み取りました。 Heap32Nextの最後の呼び出し(FALSEを返した呼び出し)は、割り当てられたメモリを解放しました。 あなたはすでにここで問題を見ましたか? 何らかの理由で、ヒープの終わりに達する前にメモリスキャンを終了したい場合(ユーザーがアクションをキャンセルし、タイムアウトが終了し、探していたものが見つかった場合)、すぐにメモリリークが発生します。 他の同様のWinAPI関数セット(FindFirstFile / FindNextFile / FindCloseなど)とは異なり、Heap32Closeのような関数はありません。 メモリを解放する唯一の方法は、Heap32Nextで最後まで到達することです。 また、Windows 95はマルチタスクモデルを変更しました。 連続する2つのHeap32Next呼び出しの間に、ヒープ上のデータが別のプロセスによって変更される可能性がありました。 ただし、この状況はToolHelpコンポーネントの機能によって処理されませんでした。
上記の問題はどちらも非常に深刻に見えますが、これらの関数の作成者によると、それらは診断目的のみを目的としています。 ToolHelpコンポーネントの名前は、開発者が開発やデバッグには使用できるが、ユーザー向けの最終製品には使用できないという考えに導くべきでした。
WindowsがNTカーネルに切り替わったときにすべてが変更されました。 Windows NT開発者は、システムの安定性にかなりの注意を払い、補助機能の設計においてもメモリリークを避けたいと考えていました。 プログラマーが常にHeap32Nextを使用してヒープの最後に到達するという保証がなく、パスをより早く完了できるようにする方法もなかったため、Heap32FirstおよびHeap32Nextから戻る前に、割り当てられたすべてのメモリを解放することにしました。 アプリケーションがHeap32Firstを呼び出すと、この関数はヒープの写真を撮り、最初のブロックを返して写真を解放しました。 アプリケーションがHeap32Nextを呼び出すと、ヒープスナップショットが再び取得され、2番目のブロックが返され、スナップショットが再び解放されました。 などなど、あなたはアイデアを得た。
このアルゴリズムを実装した結果、nブロックのメモリを表示するにはO(n²)操作が必要でした。
では、なぜWindows 7で遅くなったのでしょうか?
Windows 7より前のバージョンでは、前述のメモリ領域のスナップショットは固定サイズのバッファに保存されていました。 ヒープ内の25万ブロックのメモリに関する情報が含まれていました。 この実装では、Heap32First / Heap32Next関数を呼び出すという最悪の場合の厳しい制限が自動的に判明しました。 Windows 7では、一部の診断ユーティリティの開発者から、そのサイズに依存しているという苦情があったため、このバッファーが増加しました。 Windowsカーネルの開発者は、迅速かつ誤って機能するよりも、ゆっくりと正しく機能させる方が良いと判断しました。 したがって、バッファサイズの増加により、既にあまり美しくない複雑度O(n²)に大きな定数が追加されました。
機能に関するこの悲しい話は、協調マルチタスクの際に補助的なものとして設計されたため、OSの次のバージョンでは十分にアップグレードできませんでした。 現時点では、Heap32First / Heap32Next関数は、「家族には黒い羊がいる」というスローガンの下でWindowsカーネルに存在します。 誰もそれらを好きではありませんが、カーネルから何かを取り出して捨てることはできません:優れた後方互換性はWindowsの略です。
しかし、幸いなことに、より適切に機能する新しいものをいつでも追加できます。 したがって、この場合の「新しい」のは、 HeapWalk関数です。 複雑さはO(n)ですが、現在のプロセスのみのメモリを読み取ることができるという制限があります。 他のプロセスのメモリを読み取りたい場合は、Heap32First / Heap32Next以外の選択肢はありません。 開発者のマシン上で、診断目的で最も頻繁に似たようなことを行う必要があるという事実に安心することができます。 そして、ここでは、パフォーマンスよりも正確さが重要です。