由于所有上下文,这看起来像一个很长的问题.下面小说中有两个问题.感谢您抽出宝贵时间阅读本文并提供帮助.
情况
我正在开发一个可扩展的数据存储区实现,它可以支持在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中,我需要将OutputStream中的内容(我自己将数据填充到该流中)放入ByteBuffer中.如何以简单的方式做到这一点?
我用这个:
byte[] buffer = new byte[10240];
Run Code Online (Sandbox Code Playgroud)
据我所知,这初始化填充0s的10kb缓冲区数组.
什么是每次用垃圾数据填充这个数组(或初始化它)的最快方法?
我需要使用那个数组> 5000次并每次用不同的垃圾数据填充它,这就是为什么我正在寻找一个快速的方法来做到这一点.数组大小也必须每次都改变.
我必须在遗留系统和Android设备之间进行双向通信.遗留系统使用小端字节排序.我已经成功实现了接收部分,但发送不起作用.
奇怪,因为对我来说,似乎ByteBuffer类出现故障(我简直不敢相信)
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
byteBuffer = ByteBuffer.allocate(4);
byteBuffer.putInt(88);
byte[] result = byteBuffer.array();
Run Code Online (Sandbox Code Playgroud)
结果:[0,0,0,88]
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
byteBuffer.order(ByteOrder.BIG_ENDIAN);
byteBuffer = ByteBuffer.allocate(4);
byteBuffer.putInt(88);
byte[] result = byteBuffer.array();
Run Code Online (Sandbox Code Playgroud)
结果相同:[0,0,0,88]
但是,如果我没有弄错,小端序排序应该返回:[88,0,0,0]
那么我错过了什么呢?
我有一个方法:
public native void doSomething(ByteBuffer in, ByteBuffer out);
Run Code Online (Sandbox Code Playgroud)
由javah生成的C/C++此方法的头是:
JNIEXPORT void JNICALL Java__MyClass_doSomething (JNIEnv *, jobject, jobject, jobject, jint, jint);
Run Code Online (Sandbox Code Playgroud)
如何从jobject(即ByteBuffer实例)获取数据数组?
我有一个ByteBuffer最多可以保持(4 + size)bytes(即一个整数后跟size字符).但是,写入的字符数ByteBuffer可能小于size.
所以我想知道,无论如何要确定写入了多少字符ByteBuffer而不仅仅是它的总大小?limit,position这样就不会成为我追求的目标.
谢谢你的帮助!
在处理多个千兆字节的文件时,我注意到一些奇怪的事情:似乎从使用filechannel的文件读取到使用allocateDirect分配的重用ByteBuffer对象比从MappedByteBuffer读取要慢得多,实际上它甚至比读取字节更慢 - 使用常规读取调用的数组!
我期望它(几乎)与从mappedbytebuffers读取一样快,因为我的ByteBuffer被分配了allocateDirect,因此读取应该直接在我的bytebuffer中结束而没有任何中间副本.
我现在的问题是:我做错了什么?或者bytebuffer + filechannel是否比常规io/mmap慢?
我在下面的示例代码中还添加了一些代码,将读取的内容转换为long值,因为这是我的实际代码不断执行的操作.我希望ByteBuffer getLong()方法比我自己的字节shuffeler快得多.
测试结果:mmap:3.828 bytebuffer:55.097常规i/o:38.175
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.MappedByteBuffer;
class testbb {
static final int size = 536870904, n = size / 24;
static public long byteArrayToLong(byte [] in, int offset) {
return ((((((((long)(in[offset + 0] & 0xff) << 8) | (long)(in[offset + 1] & 0xff)) << 8 | (long)(in[offset + 2] & 0xff)) << 8 | (long)(in[offset + 3] & 0xff)) << 8 …Run Code Online (Sandbox Code Playgroud) 我需要将(直接)传递给ByteBuffer将从缓冲区读/写的本机函数.完成这些操作后,我想ByteBuffer使用常规函数访问Java代码; 特别是,limit()和position()要体现缓冲器的当前状态.
由于JNI将使用GetDirectBufferAddress()直接访问底层缓冲区,我假设我应该在读完/写入后调用flip ()/ limit()/ position().但是,我一直无法做到这一点.例如,在我从C读取几个字节进入缓冲区并相应地设置其限制和位置后,我无法从Java查询这些字节; Java对缓冲区限制和位置的想法与我在C代码中所做的不一致.
任何人都可以向我指出一个有效的例子吗?提前致谢.
我知道如何在java中做到这一点(见这里),但是我找不到java的ByteBuffer的快速等价物,因此找不到它的.putDouble(double value)方法.
基本上,我正在寻找这样的功能:func doubleToByteArray(value: Double) -> [UInt8]? {
. . .
}
doubleToByteArray(1729.1729) // should return [64, 155, 4, 177, 12, 178, 149, 234]
Run Code Online (Sandbox Code Playgroud) 我试图通过使用内存映射特定文件在两个或更多JVM之间实现一种共享缓存MappedByteBuffer.从规范中我看到,当我们使用MappedByteBuffer.load()它时应该将数据加载到直接缓冲区.我对此有几个问题.
我的代码片段::
RandomAccessFile file = new RandomAccessFile("file.txt","rw");
FileChannel fc = file.getChannel();
MappedByteBuffer buf5 = fc.map(MapMode.READ_WRITE, 0, fc.size());
//ByteBuffer buf6 = ByteBuffer.allocateDirect(100000000);
buf5.load();
try
{
Class c = Class.forName("java.nio.Bits");
Field f = c.getDeclaredField("reservedMemory");
f.setAccessible(true);
long reservedMemory = f.getLong(null);
f = c.getDeclaredField("maxMemory");
f.setAccessible(true);
System.out.println(
"Direct Memory Usage: "+ reservedMemory +"/"+ f.getLong(null)+"\n");
}
catch (Throwable t)
{
}
Run Code Online (Sandbox Code Playgroud)
对于直接内存使用(File.txt为1 GB),上述代码的输出为0字节.但是,如果我取消注释该行..
ByteBuffer buf6 = ByteBuffer.allocateDirect(100000000);
Run Code Online (Sandbox Code Playgroud)
我得到100MB的直接内存使用率.无法理解为什么会这样,至于为什么我没有得到任何直接的内存使用(即当行被注释掉)
虽然上述代码的直接内存使用率为0 B,但我确实看到进程的常驻内存(使用unix top)增加了1 GB.但是如果我在盒子上做了"free -m",我看不到任何内存使用量的增加.
在这两种情况下,我对内存最终的位置感到有些困惑.
谢谢!
bytebuffer ×10
java ×8
byte ×2
bytearray ×2
nio ×2
android ×1
arrays ×1
c ×1
c# ×1
endianness ×1
file-io ×1
ios ×1
memory ×1
outputstream ×1
performance ×1
size ×1
swift ×1
unix ×1