我有一个应用程序通过网络接收数据块,并将这些数据写入磁盘.一旦收到所有块,就可以将它们解码/重新组合成它们实际代表的单个文件.
我想知道是否使用内存映射文件是有用的 - 首先是将单个块写入磁盘,第二个是单个文件,所有这些文件都被解码.
我自己的感觉是它只对第二种情况有用,有人对此有所了解吗?
编辑:这是一个C#应用程序,我只是计划一个x64版本.(因此,遇到'最大的可用自由空间'问题应该不相关)
我正在使用文件("sample.txt")映射到内存FileChannel.map()
,然后使用关闭通道fc.close()
.在此之后,当我使用FileOutputStream写入文件时,我收到以下错误:
java.io.FileNotFoundException:sample.txt(无法在打开用户映射部分的文件上形成请求的操作)
File f = new File("sample.txt");
RandomAccessFile raf = new RandomAccessFile(f,"rw");
FileChannel fc = raf.getChannel();
MappedByteBuffer mbf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
fc.close();
raf.close();
FileOutputStream fos = new FileOutputStream(f);
fos.write(str.getBytes());
fos.close();
Run Code Online (Sandbox Code Playgroud)
我认为这可能是由于文件仍然映射到内存,即使我关闭后FileChannel
.我对吗?.如果是这样,我如何从内存中"取消映射"文件?(我在API中找不到任何方法).谢谢.
编辑:看起来像(添加一个unmap方法)作为RFE提交给sun一段时间了:http : //bugs.sun.com/view_bug.do?bug_id=4724038
在低内存情况下,iOS平台如何处理内存映射文件?在低内存情况下,我的意思是操作系统将UIApplicationDidReceiveMemoryWarningNotification
通知发送给应用程序中的所有观察者.
我们的文件使用+[NSData dataWithContentsOfMappedFile:]
以下文档映射到内存中:
映射文件使用虚拟内存技术来避免将文件页面复制到内存中,直到实际需要它们为止.
这是否意味着操作系统也会在页面不再使用时取消映射?是否可以将页面标记为不再使用?如果更改方案,则此数据是只读的.如果我们mmap()
直接使用怎么样?这会更好吗?
我已经使用C#来解决以下要求.. - 创建一个可以快速接收大量数据的应用程序 - 您必须能够分析收到的数据,同时传入更多数据. - 使用尽可能少的CPU和磁盘
我对算法的想法是......
SIZE = 10MB
Create a mmf with the size of SIZE
On data recived:
if data can't fit mmf: increase mmf.size by SIZE
write the data to mmf
Run Code Online (Sandbox Code Playgroud)
- >当使用前一个"房间/空间"时,光盘上的大小增加了10MB的块.
如何在C#中"通过SIZE增加mmf.size"?我已经找到了很多关于创建mmfs和视图的简单示例,但是我看到的唯一的地方(链接)实际上增加了mmfs区域的代码使用了无法编译的代码.任何帮助都将受到极大的关注.
编辑这会导致异常:
private void IncreaseFileSize()
{
int theNewMax = this.currentMax + INCREMENT_SIZE;
this.currentMax = theNewMax;
this.mmf.Dispose();
this.mmf = MemoryMappedFile.CreateFromFile(this.FileName, FileMode.Create, "MyMMF", theNewMax);
this.view = mmf.CreateViewAccessor(0, theNewMax);
}
Run Code Online (Sandbox Code Playgroud)
抛出此异常:进程无法访问文件'C:\ Users\moberg\Documents\data.bin',因为它正由另一个进程使用.
内存障碍保证数据缓存一致.但是,它是否保证TLB一致?
我看到一个问题,当在线程之间传递MappedByteBuffer时,JVM(java 7更新1)有时会因内存错误(SIGBUS,SIGSEG)而崩溃.
例如
final AtomicReference<MappedByteBuffer> mbbQueue = new AtomicReference<>();
// in a background thread.
MappedByteBuffer map = raf.map(MapMode.READ_WRITE, offset, allocationSize);
Thread.yield();
while (!inQueue.compareAndSet(null, map));
// the main thread. (more than 10x faster than using map() in the same thread)
MappedByteBuffer mbb = inQueue.getAndSet(null);
Run Code Online (Sandbox Code Playgroud)
没有Thread.yield()我偶尔会在force(),put()和C的memcpy()中崩溃,这些都表示我试图非法访问内存.使用Thread.yield()我没有遇到任何问题,但这听起来不是一个可靠的解决方案.
有人遇到过这个问题吗?有关TLB和内存障碍的保证吗?
编辑:操作系统是Centos 5.7,我已经看到了i7和双Xeon机器上的行为.
为什么我这样做?因为写入消息的平均时间是35-100 ns,具体取决于长度,使用普通的write()并不是那么快.如果我在当前线程中进行内存映射和清理,则需要50-130微秒,使用后台线程执行此操作需要大约3-5微秒的主线程交换缓冲区.为什么我需要交换缓冲区呢?因为我写的是很多GB数据而且ByteBuffer的大小不能超过2 GB.
在了解了这个主题之后,任何人都可以说,POSIX共享内存(shm_open)和POSIX映射文件(mmap)之间的真正区别是什么?
两者似乎都使用/ dev/tmpfs子系统,而不是旧的IPC机制.
那么在共享内存上使用mmap文件有什么好处吗?
谢谢.
什么限制了内存映射文件的大小?我知道它不能超过最大的连续未分配地址空间块,并且应该有足够的可用磁盘空间.但还有其他限制吗?
我正在研究使用ByteBuffers从内存映射文件(通过FileChannel.map())以及内存中直接ByteBuffers 构建的东西.我试图了解并发和内存模型约束.
我已经阅读了FileChannel,ByteBuffer,MappedByteBuffer等所有相关的Javadoc(和源代码).很明显,特定的ByteBuffer(和相关的子类)有一堆字段,并且状态不受内存模型的保护观点看法.因此,如果跨线程使用该缓冲区,则必须在修改特定ByteBuffer的状态时进行同步.常见的技巧包括使用ThreadLocal包装ByteBuffer,复制(同步)以获取指向相同映射字节的新实例等.
鉴于这种情况:
B_all
为整个文件提供了一个映射的字节缓冲区(比如它<2gb)B_1
,该文件的一大块并将其提供给线程T1B_2
指向相同映射字节的ByteBuffer 并将其提供给线程T2我的问题是:T1可以写入B_1,T2可以同时写入B_2并保证看到彼此的变化吗?T3可以使用B_all来读取这些字节并保证看到T1和T2的变化吗?
我知道,除非您使用force()指示操作系统将页面写入磁盘,否则不一定会在进程中看到映射文件中的写入.我不在乎.假设这个问题,这个JVM是编写单个映射文件的唯一进程.
注意: 我不是在寻找猜测(我可以自己做得很好).我想引用一些关于内存映射直接缓冲区保证(或不保证)的内容.或者,如果您有实际经验或负面测试用例,那么这也可以作为充分的证据.
更新:我已经完成了一些测试,让多个线程并行写入同一个文件,到目前为止,似乎这些写入可以立即从其他线程中看到.我不确定我是否可以依靠它.
如何分配与C中特定边界对齐的内存(例如,缓存行边界)?我正在寻找malloc/free类似的实现,理想情况下尽可能便携 - 至少在32位和64位架构之间.
编辑添加:换句话说,我正在寻找一些表现得像(现在过时的?)memalign函数的东西,它可以免费使用.
由于所有上下文,这看起来像一个很长的问题.下面小说中有两个问题.感谢您抽出宝贵时间阅读本文并提供帮助.
情况
我正在开发一个可扩展的数据存储区实现,它可以支持在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
我希望能够确认我对所有这一切的理解.
例如,文件缓存可能很棒,在两种情况下(内存映射或直接读取),主机操作系统将保留尽可能多的热数据,大文件的性能差异可以忽略不计.
或许我对内存映射文件(连续内存)的敏感要求的理解是不正确的,我可以忽略所有这些.
java ×4
bytebuffer ×2
c# ×2
.net-4.0 ×1
alignment ×1
c ×1
centos ×1
concurrency ×1
file-io ×1
filechannel ×1
ios ×1
low-memory ×1
mmap ×1
posix ×1
tlb ×1