我有一个.net Web服务(Web API 2),该服务接受从数据库获取数据的请求。返回的数据不是很大,通常仅为1-3 kb(数据库中约1-15行)。我在IIS中进行了设置,并对其进行了一些批量测试,在一个小时左右的时间内它受到了很大的冲击,因此它收到了数千个请求。当我查看w3wp.exe(IIS工作进程)时,内存一直保持增长,直到达到5 GB以上,并且CPU使用率接近100%,然后最终Web服务停止工作。服务器有16 gb的RAM,我们刚刚从8增加了。5 gb的内存正常吗?看起来很多,我本来以为垃圾回收会更好地处理这个问题。我在使用性能监视器或其他方法来解决此类问题方面经验不足。
关于我能看到的任何建议吗?会不会发生内存泄漏?当应用程序池达到一定的内存量时,我应该尝试回收吗?
更新-我们有另一个.net Web服务,它甚至不访问数据库或任何外部文件或任何东西,并且以相同的方式工作-内存在不断增加。我们的计划是可能将每个应用程序池设置为每x分钟或在达到一定内存量时回收。
以下是API中典型功能的示例:
[Route("patient/{patientKey}/activations")]
public async Task<IHttpActionResult> GetActivations(int patientKey)
{
try
{
ActivationList actList = new ActivationList();
actList.Activations = await _actProc.GetByPatient(patientKey);
return Ok<ActivationList>(actList);
}
catch (Exception ex)
{
return new BadRequestWithInfoResult(Translators.makeXML<ErrorReturn>(CreateError(ex)));
}
}
public async Task<List<Activation>> GetByPatient(long patientKey)
{
using (var dbConn = _database.CreateConnection())
{
List<DBActivation> lst_Activation = await dbConn.FetchAsync<DBActivation>("select * from fact.Activation where PatientKey = " + patientKey.ToString());
List<Activation> Activations = lst_Activation.Select(x => _mapper.Map<Activation>(x)).ToList<Activation>();
return Activations;
}
}
答案 0 :(得分:0)
激活对象中有什么?如果激活有多个子项,则在映射过程中,它可能会尝试填充所有子项以及这些子项,等等。...如果您使用EF延迟加载,则EF将为每个子项人口创建一个子查询。
答案 1 :(得分:0)
OP答案-我发现了一些令人反感的代码(不,对于这样的Web服务来说,使用超过5 GB的内存是不正常的)。不久前,当尝试在Web服务返回的xml中创建自己的名称空间时,我在@Konamiman的回答下添加了本文中指定的类CustomNamespaceXmlFormatter:Remove namespace in xml下面的评论者提到了内存泄漏问题。尽管ANTS Memory Profiler从未显示我的Web服务生成多个动态程序集,但是我仍然更新了代码,以使用类似于单例模式的东西来创建XmlSerializer的实例,如下所示,现在我的内存使用情况受到控制(实际上下降了)完成请求处理后!)。
public class CustomNamespaceXmlFormatter : XmlMediaTypeFormatter
{
private readonly string defaultRootNamespace;
public CustomNamespaceXmlFormatter() : this(string.Empty)
{
}
public CustomNamespaceXmlFormatter(string defaultRootNamespace)
{
this.defaultRootNamespace = defaultRootNamespace;
}
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
if (type == typeof(String))
{
//If all we want to do is return a string, just send to output as <string>value</string>
return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
}
else
{
XmlRootAttribute xmlRootAttribute = (XmlRootAttribute)type.GetCustomAttributes(typeof(XmlRootAttribute), true)[0];
if (xmlRootAttribute == null)
xmlRootAttribute = new XmlRootAttribute(type.Name)
{
Namespace = defaultRootNamespace
};
else if (xmlRootAttribute.Namespace == null)
xmlRootAttribute = new XmlRootAttribute(xmlRootAttribute.ElementName)
{
Namespace = defaultRootNamespace
};
var xns = new XmlSerializerNamespaces();
xns.Add(string.Empty, xmlRootAttribute.Namespace);
return Task.Factory.StartNew(() =>
{
//var serializer = new XmlSerializer(type, xmlRootAttribute); **OLD CODE**
var serializer = XmlSerializerInstance.GetSerializer(type, xmlRootAttribute);
serializer.Serialize(writeStream, value, xns);
});
}
}
}
public static class XmlSerializerInstance
{
public static object _lock = new object();
public static Dictionary<string, XmlSerializer> _serializers = new Dictionary<string, XmlSerializer>();
public static XmlSerializer GetSerializer(Type type, XmlRootAttribute xra)
{
lock (_lock)
{
var key = $"{type}|{xra}";
if (!_serializers.TryGetValue(key, out XmlSerializer serializer))
{
if (type != null && xra != null)
{
serializer = new XmlSerializer(type, xra);
}
_serializers.Add(key, serializer);
}
return serializer;
}
}
}
答案 2 :(得分:-1)
由于未在库中使用ConfigureAwait,因此似乎是一个死锁,
[Route("patient/{patientKey}/activations")]
public async Task<IHttpActionResult> GetActivations(int patientKey)
{
try
{
ActivationList actList = new ActivationList();
actList.Activations = await _actProc.GetByPatient(patientKey).ConfigureAwait(false);
return Ok<ActivationList>(actList);
}
catch (Exception ex)
{
return new BadRequestWithInfoResult(Translators.makeXML<ErrorReturn>(CreateError(ex)));
}
}
public async Task<List<Activation>> GetByPatient(long patientKey)
{
using (var dbConn = _database.CreateConnection())
{
List<DBActivation> lst_Activation = await dbConn.FetchAsync<DBActivation>("select * from fact.Activation where PatientKey = " + patientKey.ToString()).ConfigureAwait(false);
List<Activation> Activations = lst_Activation.Select(x => _mapper.Map<Activation>(x)).ToList<Activation>();
return Activations;
}
}