私はHttpClientを使用して簡単なHTTPS API呼び出しを実行するメソッドを呼び出す10-150の長いリビングクラスオブジェクトを持っています。 PUTコールの例:HttpClientHandler/HttpClientメモリリーク
using (HttpClientHandler handler = new HttpClientHandler())
{
handler.UseCookies = true;
handler.CookieContainer = _Cookies;
using (HttpClient client = new HttpClient(handler, true))
{
client.Timeout = new TimeSpan(0, 0, (int)(SettingsData.Values.ProxyTimeout * 1.5));
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", Statics.UserAgent);
try
{
using (StringContent sData = new StringContent(data, Encoding.UTF8, contentType))
using (HttpResponseMessage response = await client.PutAsync(url, sData))
{
using (var content = response.Content)
{
ret = await content.ReadAsStringAsync();
}
}
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception ex)
{
LastErrorText = ex.Message;
}
}
}
using
文を経由して適切な処分を含め、これらのメソッドを実行しているの2~3時間後に、プログラムは、1GBのメモリ、1.5ギガバイトにぞっとしており、最終的にメモリ不足様々でクラッシュエラー。多くの場合、接続は信頼性の低いプロキシ経由で行われるため、接続が期待通りに完了しない可能性があります(タイムアウトやその他のエラーが一般的です)。
.NETメモリプロファイラーでは、HttpClientHandler
が主な問題で、「直接の委任ルートを持つインスタンスを廃棄しました」(赤い感嘆符)と「廃棄されたがまだGCedされていないインスタンス」(黄色エクスクラメーション・マーク)。プロファイラが示すルートは、HttpWebRequestに由来するAsyncCallback
です。
TlsStream
は、「廃棄されましたがGCedではない」というルートのオブジェクトであるため、RemoteCertValidationCallback
とHTTPS証明書の検証と関係があります。
これを念頭に置いて、どうすればHttpClientをより正しく使用してこれらのメモリの問題を回避できますか?私は毎時GC.Collect()
を強制する必要がありますか?私はそれが悪い習慣と見なされることは知っていますが、私は、このメモリを再処分する方法が他にどれほど適切に処理されているのか分かりませんし、これらの短命オブジェクトのより良い使用パターンは、 .NETオブジェクト自体に欠陥がある。強制GC.Collect()
UPDATE は効果がありませんでした。
プロセス全体の管理されたバイト数は20-30 MB程度ですが、プロセス全体のメモリ(タスクマネージャー内)は上昇し続け、管理されていないメモリリークを示します。したがって、この使用パターンは、アンマネージメモリリークを引き起こしています。
提案ごとにHttpClientとHttpClientHandlerの両方のクラスレベルのインスタンスを作成しようとしましたが、これには大きな影響はありませんでした。これらをクラスレベルに設定しても、プロキシの設定に変更が必要なことが多いため、再作成され、再利用されることはほとんどありません。 HttpClientHandlerでは、リクエストが開始されるとプロキシ設定やプロパティの変更が許可されないため、当初は独立したusing
ステートメントのようにハンドラを再作成しています。
HttpClienthandlerは、依然としてAsyncCallback - > HttpWebRequestに「直接委任ルート」を使用して処理されています。私はおそらくHttpClientが速い要求と短命のオブジェクトのために設計されていないかどうか疑問に思っています。視界に終わりはありません。誰かがHttpClientHandlerを実行可能にするよう提案することを望むことを望みます。
メモリプロファイラショット:
なぜ、各呼び出しで 'HttpClientHandler'と' HttpClient'を処理していますか? –
コードの重要ではない部分を切り捨てましたが、要求に応じてヘッダーが変動します(例:HttpClientHandler)。 HttpClientHandlerオブジェクトのメンテナンスが頻繁に行われるため、毎回それを再作成する方が簡単だと思っていましたが、それでもまだそれを満たすことはできませんでした。なぜこれらのオブジェクトをリークすることなく繰り返し再作成できないのかという質問 – user1111380
GC.collect()を試してみると、これらのTlsStreamオブジェクトが収集されていますか? – zaitsman