我是D的新手。
我使用http://arsdnet.net/web.d/cgi.d.html编写了一个简单的文件服务器
我发送一个文件(大小约为xxMB),如下所示:
import std.file;
void SendFile(string request)
{
auto bytes = read(request);
cgi_.setResponseContentType("application/zip");
cgi_.write(bytes);
bytes = null;
}
它可以很好地发送文件,但似乎没有垃圾回收。因此,在发送了一些文件后,它无法分配内存进行读取。
我遗漏了什么?有没有办法手动释放内存?
发布于 2013-06-03 10:11:35
我怀疑问题出在cgi.d的makeChunk程序中。我更改了它以避免不必要的分配,它在我这里运行的快速测试中表现得更好,所以如果你尝试新版本,你希望会有更好的运气。
点击此处获取cgi.d的新版本:
https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff
如果这对您有效,最好查看一下提交,看看修复方法是什么:https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff/commit/848566eaf76ae708ccf0109fbb369e2c883f5379
有一个看起来很简单的函数,叫做makeChunk,它通过创建一个byte[]并附加所需的部分来工作:长度、数据和一个终止符。如果你想知道它为什么这样做,可以在网上搜索http 1.1规范,并查看"Transfer-Encoding: chunked“报头部分。(如果您在embedded_httpd以外的任何模式下使用cgi.d,则它不会调用此函数-用于cgi。fastcgi和scgi,Apache/IIS/nginx/任何服务器处理这样的细节,所以cgi.d直接将数据传递出去。但在embedded_httpd中,它需要注意协议的线路细节。实际上,我最常在普通的旧cgi模式中使用它,所以像这样的bug可能会从我身边溜走!)
为什么我要这样做,而不是现在这样做呢?呃,我不记得了,我想我直到那个函数写好之后才添加了接收器委托,因为它适用于小的情况,没什么大不了的。
无论如何,D中的~=操作符通过垃圾收集器分配新内存。如果文件很大--在我的测试中,我只使用了100MB的随机数据--这个引用可能会占用大量的地址空间。
在32位上,地址空间为4 GB。当然,100MB大约是它的1/40。D垃圾收集器是保守的。也就是说,它扫描所有可用的空间,并假定它看到的任何数字都可能是指针。堆栈上的任何随机数据都假定它只是一个指针,所以如果它指向那个巨大的数组,gc不会释放它,以防它实际上仍在使用中。
这对于小数组非常有效(以及64位的大数组,因为随机数实际上指向大数据的几率几乎为零),但这里的几率只有1/40。因此,如果gc扫描的数据中有100个或多或少的随机数,它很可能会仅仅通过纯粹的随机概率意外地将这个巨大的数组绑定到内存中。
这就是为什么这首先是一个问题,以及为什么我的第一个评论之一是尝试“删除字节”,将其从运行中删除。但是,棘手的部分是cgi.d无意中复制了一份,并且从未告诉任何人……所以你可以非常勤奋,但仍然会泄漏内存,因为同样的随机效应也会发生在这个看起来很简单的makeChunk函数上。
因此,解决方案是避免其中的数组追加操作。相反,它现在只是将这些片段直接写到套接字中。
发布于 2013-06-03 00:13:51
我正在运行这个:
import std.stdio;
import std.file;
void main()
{
foreach (i; 0 .. 1_000_000) {
auto bytes = read("bigfile.mkv");
write(bytes.length, ' ', i, '\r');
stdout.flush();
}
}
这会打印737876138,然后是一个递增的数字。循环的速度大约是每秒6.75次迭代。根据top
的说法,所有与内存相关的参数都会很快稳定下来(所以我假设每次传递都有一个GC周期)。RSS是1411M,即大约是文件大小的两倍。
修改后的版本要慢得多:
import std.stdio;
import std.file;
import core.memory;
void main()
{
GC.disable();
foreach (i; 0 .. 1_000_000) {
auto bytes = read("Movies/Act of Valor 2012.mkv");
write(bytes.length, ' ', i, '\r');
stdout.flush();
}
}
这可能是因为缓存不友好,所以它的运行速度要慢得多,而且由于内存不足,它确实很快就死了。
https://stackoverflow.com/questions/16877544
复制相似问题