使用CopyTo向HttpWebRequest流写入数据引发内存持续增长问题排查
问题分析与解决方案
是不是内存泄漏?
这不属于严格意义上的内存泄漏,更可能是未正确释放托管/非托管资源,或是HttpWebRequest相关连接池、请求资源未及时回收导致的内存持续增长。
核心修复步骤
1. 完整释放HttpWebRequest及响应资源
你的代码仅释放了请求流,但未处理HttpWebRequest本身和响应对象,这会导致大量资源滞留。必须用using包裹所有实现IDisposable的对象:
void SendToOtherServer(Stream fileContents) { using (HttpWebRequest request = (HttpWebRequest)WebRequest.Create("the other server address")) { request.Method = "POST"; // 务必设置请求方法 // 若目标服务器支持,可关闭Expect100Continue减少交互开销 // ServicePointManager.Expect100Continue = false; using (Stream destStream = request.GetRequestStream()) { // 显式设置CopyTo缓冲区大小,避免一次性分配过大内存 fileContents.CopyTo(destStream, 81920); // 80KB缓冲区,可根据业务调整 } // 必须处理响应,否则连接无法回收到连接池 using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { using (Stream responseStream = response.GetResponseStream()) { // 读取响应流(即使不需要内容),确保连接资源释放 responseStream.CopyTo(Stream.Null); } } } }
2. 不要手动释放传入的fileContents
fileContents是WCF框架传入的流,由框架负责生命周期管理。你之前用using包裹它会提前释放流,可能导致WCF后续处理异常,直接移除该using即可。
3. 优化HttpWebRequest连接池配置
默认ServicePointManager.DefaultConnectionLimit仅为2,大量并发请求会导致连接排队、资源滞留。在应用启动时(如Global.asax的Application_Start)设置合理值:
ServicePointManager.DefaultConnectionLimit = Environment.ProcessorCount * 10; // 根据服务器配置调整
4. 异步场景的资源释放(若使用BeginGetResponse)
如果用异步方式发送请求,务必在回调中完整释放响应及流资源:
void SendAsync(Stream fileContents) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create("the other server address"); request.Method = "POST"; request.BeginGetRequestStream(asyncResult => { try { using (Stream destStream = request.EndGetRequestStream(asyncResult)) { fileContents.CopyTo(destStream, 81920); } request.BeginGetResponse(responseResult => { try { using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(responseResult)) using (Stream responseStream = response.GetResponseStream()) { responseStream.CopyTo(Stream.Null); } } catch (WebException ex) { // 处理异常,同时释放错误响应 if (ex.Response != null) { ((HttpWebResponse)ex.Response).Dispose(); } } finally { request.Dispose(); } }, null); } catch { request.Dispose(); throw; } }, null); }
为什么强制GC没用还变慢?
手动调用GC.Collect()会触发全量垃圾回收,暂停所有线程执行,导致性能急剧下降。正确释放资源后,GC会自动在合适时机回收托管内存,无需手动干预。
内容的提问来源于stack exchange,提问作者Programmer
相关产品推荐
相关产品推荐

