由于所有上下文,这看起来像一个很长的问题.下面小说中有两个问题.感谢您抽出宝贵时间阅读本文并提供帮助.
情况
我正在开发一个可扩展的数据存储区实现,它可以支持在32位或64位系统上处理从几KB到TB或更大的数据文件.
数据存储区采用Copy-on-Write设计; 始终将新数据或修改后的数据附加到数据文件的末尾,并且永远不会对现有数据进行就地编辑.
系统可以托管1个或多个数据库; 每个由磁盘上的文件表示.
实施细节并不重要; 唯一重要的细节是我需要不断附加到文件并将其从KB增加到MB,再增加到GB到TB,同时随机跳过文件以进行读取操作以回答客户端请求.
首先,思考
乍一看,我知道我想使用内存映射文件,因此我可以将有效管理数据的内存中状态的负担推到主机操作系统和我的代码之外.
然后我的所有代码都需要担心的是在写入时序列化附加到文件的操作,并允许任意数量的同时读者在文件中搜索以回答请求.
设计
因为单个数据文件可以超过MappedByteBuffer的2GB限制,所以我希望我的设计必须包含一个抽象层,它采用写入偏移并将其转换为特定2GB段内的偏移量.
到现在为止还挺好...
问题
这是我开始被挂起的地方,并认为采用不同的设计(下面提出)可能是更好的方法.
通过在SO上阅读20个左右的"内存映射"相关问题,似乎mmap调用对于在分配时想要连续的内存运行是敏感的.所以,例如,在32位主机操作系统上,如果我试图mmap 2GB文件,由于内存碎片,我的机会很小,映射将成功,而我应该使用像一系列128MB映射的东西来拉动整个档案.
当我想到这个设计时,甚至说使用1024MB mmap大小,对于托管几个庞大数据库的DBMS,所有数据都由1TB文件表示,我现在在内存中有数千个内存映射区域,在我自己的Windows 7测试中尝试为了在多GB文件中创建几百个mmaps,我不仅遇到了异常,每次我尝试分配太多时实际上让JVM陷入段错误,并且在一个案例中我在Windows 7机器中获得了视频切断并重新初始化我以前从未见过的操作系统错误弹出窗口.
无论"你永远不会处理那么大的文件"或"这是一个人为的例子"的论点,我可以用这些类型的副作用编写代码的事实使我的内部警报处于高度警戒状态.考虑替代impl(下面).
BESIDES问题,我对内存映射文件的理解是每次文件生成时我都必须重新创建映射,所以在这个文件只是在设计中附加的情况下,它实际上在不断增长.
我可以通过在块中增加文件(一次说8MB)来解决这个问题,并且每8MB重新创建一次映射,但是不断重新创建这些映射的需要让我感到紧张,特别是没有明确的unmap功能Java支持.
问题#1 of 2
鉴于我到目前为止的所有发现,我会将内存映射文件视为主要读取重量级解决方案或只读解决方案的良好解决方案,但考虑到需要不断重新创建映射,我认为这不是写入量大的解决方案.
然后我看看我周围的景观,像MongoDB这样的解决方案拥抱整个地方的内存映射文件,我觉得我在这里缺少一些核心组件(我知道它一次分配2GB范围,所以我想他们正在利用这种逻辑来解决重映射成本问题并帮助维持磁盘上的顺序运行.
在这一点上,我不知道问题是Java是否缺少unmap操作,这使得它更加危险并且不适合我的用途,或者如果我的理解不正确并且有人可以指向我North.
替代设计
如果我对mmap的理解是正确的,我将在上面提出的内存映射的另一种设计如下:
定义一个合理可配置大小的直接ByteBuffer(粗略地为2,4,8,16,32,64,128KB),使其可以轻松地与任何主机平台兼容(不需要担心DBMS本身导致颠簸的情况)并使用原始的FileChannel,一次执行文件1 buffer-capacity-chunk的特定偏移读取,完全放弃内存映射文件.
缺点是现在我的代码必须担心诸如"我从文件中读取足够的内容以加载完整记录吗?"
另一个缺点是我无法利用操作系统的虚拟内存逻辑,让它自动为我保留更多"热"内存数据; 相反,我只是希望操作系统使用的文件缓存逻辑足够大,可以在这里为我做一些有用的事情.
问题#2 of 2
我希望能够确认我对所有这一切的理解.
例如,文件缓存可能很棒,在两种情况下(内存映射或直接读取),主机操作系统将保留尽可能多的热数据,大文件的性能差异可以忽略不计.
或许我对内存映射文件(连续内存)的敏感要求的理解是不正确的,我可以忽略所有这些.
我有一个C++程序,在某一点上使用
hMapFile = OpenFileMapping(dwDesiredAccess, bInheritHandle, lpName);
Run Code Online (Sandbox Code Playgroud)
的lpName是一样的东西"myfile"不带扩展名或路径,即内存映射文件.我可以在路径中找到WinObj -Browser(WinObj)的文件Sessions\1\BaseNamedObjects\myfile.如果我尝试用Java读取该特定文件,我试过了
File file = new File("myfile");
FileChannel filechannel = new RandomAccessFile(file, "r").getChannel();
MappedByteBuffer buffer = filechannel.map(FileChannel.MapMode.READ_ONLY, 0, filechannel.size());
Run Code Online (Sandbox Code Playgroud)
但我总是得到同样的错误:无法在RandomAccessFile命令中找到指定的文件.也许需要另一个文件访问?哪一个?
据我所知,其他帖子,他们总是使用路径和扩展名,但是,我不知道路径和扩展名在我的情况下......
我在寻找什么,我是一种访问内核对象命名空间内核对象命名空间的方法
我知道,有这个JNI示例(使用JNI的Memorymap),但我想保持简单和使用nio(如果可能的话).
UPDATE
所以我只是尝试了C#中的所有内容,它非常简单,没有带我超过5行代码.
我想知道是否有人尝试过新的.NET 4.0内存映射文件功能?我知道它们和OS一样古老,但.NET中的原生处理是新的.
有没有人能够测试这个并说一些性能?我对二进制文件的随机部分,写入速度等的访问时间非常感兴趣.与原生WinAPI MMF的性能比较也不错.
谢谢!
我在磁盘中有一个40MB的文件,我需要使用字节数组将其"映射"到内存中.
起初,我认为将文件写入ByteArrayOutputStream是最好的方法,但我发现在复制操作期间的某个时刻需要大约160MB的堆空间.
如果不使用RAM文件大小的三倍,有人知道更好的方法吗?
更新:感谢您的回答.我注意到我可以减少内存消耗,稍微告诉ByteArrayOutputStream的初始大小要比原始文件大小稍大一些(使用我的代码强制重新分配的确切大小,得到检查原因).
还有另一个高内存点:当我用ByteArrayOutputStream.toByteArray返回byte []时.看看它的源代码,我可以看到它正在克隆数组:
public synchronized byte toByteArray()[] {
return Arrays.copyOf(buf, count);
}
Run Code Online (Sandbox Code Playgroud)
我想我可以扩展ByteArrayOutputStream并重写此方法,以便直接返回原始数组.这里有没有潜在的危险,因为流和字节数组不会被多次使用?
我试图找出如何在Mac上重新映射内存映射文件(当我想扩展可用空间时).
我在Linux世界中看到了我们的朋友,mremap但我在Mac上的标题中找不到这样的功能./Developer/SDKs/MacOSX10.6.sdk/usr/include/sys/mman.h有以下内容:
mmapmprotectmsyncmunlockmunmapmremapman mremap 证实了我的恐惧.
我目前不得不munmap,mmmap如果我想调整映射文件的大小,这涉及使所有加载的页面无效.肯定有更好的办法.一定?
我正在尝试编写适用于Mac OS X和Linux的代码.如果我不得不这样做,我可以满足于在每种情况下使用最佳功能的宏,但我宁愿正确地做到这一点.
指针不能直接保存到文件中,因为它们指向绝对地址.为了解决这个问题,我写了一个relative_ptr模板,它包含一个偏移而不是一个绝对地址.
基于这样一个事实,即只能逐个安全地复制普通的可复制类型,我假设这种类型需要通过简单的可复制来安全地保存在内存映射文件中并在以后检索.
这种限制结果有点问题,因为编译器生成的复制构造函数不会以有意义的方式运行.我没有发现任何禁止我违反复制构造函数并将其设为私有的内容,因此我将其设为私有以避免意外复制,从而导致未定义的行为.
后来,我发现boost::interprocess::offset_ptr他的创作是由同样的需求驱动的.然而,事实证明,offset_ptr它不是简单的可复制,因为它实现了自己的自定义复制构造函数.
我的假设是智能指针需要轻易复制才能安全地持久存在错误吗?
如果没有这样的限制,我想知道我是否也可以安全地执行以下操作.如果没有,那么类型必须满足哪些要求才能在上述场景中使用?
struct base {
int x;
virtual void f() = 0;
virtual ~base() {} // virtual members!
};
struct derived : virtual base {
int x;
void f() { std::cout << x; }
};
using namespace boost::interprocess;
void persist() {
file_mapping file("blah");
mapped_region region(file, read_write, 128, sizeof(derived));
// create object on a memory-mapped file
derived* d = new (region.get_address()) derived();
d.x = 42;
d->f();
region.flush();
}
void retrieve() { …Run Code Online (Sandbox Code Playgroud) 更新2/TL; DR
是否有一些方法可以防止由于关闭在这些文件上打开的内存映射而刷新Windows临时删除关闭文件的脏页.
是.如果您在初始创建后不需要对文件本身执行任何操作,并且实现了一些命名约定,则可以通过本答案中说明的策略实现.
注意:我仍然非常有兴趣找出导致行为有如此大的差异的原因,具体取决于地图的创建方式和处理/取消映射的顺序.
我一直在研究一些进程间共享内存数据结构的策略,它允许通过使用一系列"内存块"来增加和缩小它在Windows上的承诺容量.
一种可能的方法是使用页面文件支持的命名内存映射作为块内存.这种策略的一个优点是可以用来SEC_RESERVE保留一大块内存地址空间并使用VirtualAllocwith 逐步分配它MEM_COMMIT.缺点似乎是(a)要求具有SeCreateGlobalPrivilege允许在Global\命名空间中使用可共享名称的权限,以及(b)所有提交的内存都有助于系统提交费用的事实.
为了克服这些缺点,我开始研究使用临时文件支持的内存映射.即内存映射在使用FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY标志组合创建的文件上.这似乎是一种推荐的策略,根据例如此博客文章应该防止将映射的内存刷新到磁盘(除非内存压力导致脏映射页面被分页).
然而,我观察到在拥有进程退出之前关闭映射/文件句柄会导致脏页被刷新到磁盘.即使视图/文件句柄不是创建脏页面的那个句柄以及在不同视图中页面被"弄脏"之后打开这些视图/文件句柄时,也会发生这种情况.
似乎更改处理顺序(即首先取消映射视图或首先关闭文件句柄)对启动磁盘刷新的时间有一些影响,但不会影响发生刷新的事实.
所以我的问题是:
- 有没有办法使用临时文件支持的内存映射,并防止它们在关闭映射/文件时刷新脏页,考虑到进程/多个进程中的多个线程可能有这样一个文件的打开句柄/视图?
- 如果不是,观察到的行为的原因是什么?
- 你知道我可能忽略的另一种策略吗?
请参阅下面的(c ++)示例代码,该代码允许在我的系统上重现问题(x64,Win7):
static uint64_t start_ts;
static uint64_t elapsed() {
return ::GetTickCount64() - start_ts;
}
class PageArena {
public:
typedef uint8_t* pointer;
PageArena(int id, const char* base_name, size_t page_sz, size_t chunk_sz, size_t n_chunks, bool dispose_handle_first) : …Run Code Online (Sandbox Code Playgroud) language-agnostic windows winapi temporary-files memory-mapped-files
使用内存映射文件读取(常规大小的文件)而使用CreateFile ReadFile组合执行相同操作有什么缺点(如果有的话)?
考虑到磁盘上的一个非常大的文件(可能超过4GB),我想扫描这个文件并计算出特定二进制模式的发生时间.
我的想法是:
使用内存映射文件(CreateFileMap或boost mapped_file)将文件加载到虚拟内存.
对于每个100MB映射内存,创建一个线程进行扫描并计算结果.
这可行吗?有没有更好的方法呢?
更新:
内存映射文件将是一个不错的选择,通过1.6GB文件扫描可以在11s内处理.
谢谢.
所以我在Gamasutra上阅读了John Carmack的采访,其中他谈到了他所谓的"生活在内存映射文件中的实时C++对象".以下是一些引用:
JC:是的.我实际上从中获得了多项好处...最后一个iOS Rage项目,我们附带了一些新技术,这些技术使用一些聪明的东西来制作生活在内存映射文件中的实时C++对象,由闪存文件系统支持在这里,我想要构建我们未来在PC上的所有工作.
...
我在这里向自己发出命令,我希望在我们的PC平台上加载两秒钟的游戏,所以我们可以更快地迭代.而现在,即使固态硬盘,你所有你在加载时间做的事情为主,所以需要这种不同学科可以说"一切都将被毁灭和相对地址使用"所以你只要说,"映射文件,我的所有资源都在那里,它在15毫秒内完成."
(完整的采访可以在这里找到)
有没有人知道Carmack在谈论什么以及你将如何设置这样的东西?我在网上搜索了一下,但我似乎无法找到任何相关信息.
c++ ×3
java ×3
winapi ×3
windows ×2
.net ×1
.net-4.0 ×1
binaryfiles ×1
bytearray ×1
bytebuffer ×1
c ×1
c# ×1
file-io ×1
large-files ×1
libc ×1
macos ×1
mmap ×1
nio ×1
performance ×1
persistence ×1
pod ×1