我正在对我正在使用的库进行一些更改。为了减少内存使用,库将其临时数据写入磁盘而不是将其保存在内存中。然而,对于我的使用场景来说,将其保留在内存中会更有效。它还存在一些并发问题,因为它的临时文件具有常量名称,因此不能在不同线程中同时运行(因为线程会损坏彼此的数据)。
因此,我需要更改库,以便将所有数据保留在内存中。我最初并没有编写该库,因此我不太愿意对代码进行大幅更改。因此,我希望通过尽可能少的重构来做到这一点。写入磁盘的代码非常简单。这是一个(稍微简化的)示例:
final FileChannel fileChannel = this.randomAccessFile.getChannel();
fileChannel.position(calculatePosition());
while (blockData.hasRemaining())
{
fileChannel.write(blockData);
}
Run Code Online (Sandbox Code Playgroud)
块的读取非常相似(即它使用从 RandomAccessFile 获取的 FileChannel)。
感觉最简单的解决方案是如果我可以使用 FileChannel 的某种实现,它映射到内存中的某个位置而不是文件。我知道我可以使用 FileChannel 的映射方法将文件映射到内存中的某个位置。然而,事实恰恰相反。这给了我一个文件的“内存API”。我想要一个针对某些内存的 FileChannel 接口。有没有可用的实现?
我需要在给定FileChannel的情况下读取/解压缩.gz文件.
我已经玩过使用GZIPInputStream提取GZIP档案,但这不会占用FileChannel.我无权访问FileChannel的原始FileInputStream.
如果有人能告诉我一个从FileChannel阅读GZIP 的好方法(或者至少是任何方式),我将非常感激.
改编自Sun Oracle论坛上的问题.
我使用 Java 的 FileChannel 类编写了一个文件,该类使用 RandomAccessFiles。我在文件的不同位置写入了对象。这些物体大小不一,但都属于同一类别。我使用以下想法编写了对象:
ByteArrayOutputStream bos= new ByteArrayOutputStream();
ObjectOutput out = new ObjectOutputStream(bos);
out.writeObject(r);
byte[] recordBytes= bos.toByteArray();
ByteBuffer rbb= ByteBuffer.wrap(recordBytes);
while(rbb.hasRemaining()) {
fileChannel.write(rbb);
}
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)
现在我想读取这样的文件。我不想指定要读取的字节数。我希望能够使用对象输入流直接读取对象。如何实现这一目标?
我必须使用随机访问文件,因为我需要写入文件中的不同位置。我还在一个单独的数据结构中记录对象被写入的位置。
我有以下代码:
String inputFile = "somefile.txt";
FileInputStream in = new FileInputStream(inputFile);
FileChannel ch = in.getChannel();
ByteBuffer buf = ByteBuffer.allocateDirect(BUFSIZE); // BUFSIZE = 256
/* read the file into a buffer, 256 bytes at a time */
int rd;
while ( (rd = ch.read( buf )) != -1 ) {
buf.rewind();
for ( int i = 0; i < rd/2; i++ ) {
/* print each character */
System.out.print(buf.getChar());
}
buf.clear();
}
Run Code Online (Sandbox Code Playgroud)
但是角色会显示在?的位置.这是否与使用Unicode字符的Java有关?我该如何纠正?
考虑应用程序,它创建5-6个线程,循环中的每个线程为5mb页面大小分配MappedByteBuffer.
MappedByteBuffer b = ch.map(FileChannel.MapMode.READ_ONLY, r, 1024*1024*5);
Run Code Online (Sandbox Code Playgroud)
迟早,当应用程序使用大文件时,会抛出oom
java.io.IOException: Map failed at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:758)
Caused by: java.lang.OutOfMemoryError: Map failed
at sun.nio.ch.FileChannelImpl.map0(Native Method)
at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:755)
Run Code Online (Sandbox Code Playgroud)
根据规范,MappedBuffer应该在GC本身时立即配置直接内存.看起来问题是,MappedBuffer-s的GC编辑太晚了,后来直接内存完成了.
如何避免这种情况?可能会说MappedBuffer隐式处理或使用某种MappedBuffer池
我真的遇到了麻烦:我想用FileChannels和MappedByteBuffers 读取几GB的HUGE文件- 我发现的所有文档都暗示使用该FileChannel.map()方法映射文件相当简单.当然,限制为2GB,因为所有Buffer方法都使用int来定位,限制和容量 - 但系统隐含的限制如何呢?
实际上,我遇到很多关于OutOfMemoryExceptions 的问题!并没有真正定义限制的文档!那么 - 如何将一个符合int-limit的文件安全地映射到一个或几个MappedByteBuffers而不仅仅是异常?
在尝试之前,我可以问系统我可以安全地映射文件的哪个部分FileChannel.map()?怎么样?为什么关于这个功能的文档很少?
我正在研究一个sc2replay解析工具.我在MPQLIB http://code.google.com/p/mpqlib/之上构建它
不幸的是,该工具使用文件通道来读取bzip文件并使用 map(MapMode.READ_ONLY, hashtablePosition, hashTableSize);
调用该函数后关闭文件通道不会在该过程中释放该文件.具体来说,我无法重命名/移动文件.
问题出现在Java 7中,它在Java 6上运行良好.
这是一个复制它的简单代码片段:
FileInputStream f = new FileInputStream("test.SC2Replay");
FileChannel fc = f.getChannel();
fc.map(MapMode.READ_ONLY, 0,1);
fc.close();
new File("test.SC2Replay").renameTo(new File("test1.SC2Replay"));
Run Code Online (Sandbox Code Playgroud)
注释掉fc.map将允许您重命名该文件.
PS从这里我应该关闭FileChannel吗?
它声明您不需要关闭文件通道和文件流,因为关闭一个将关闭另一个.我也试过关闭其中一个或两个,但仍然没有奏效.
在Java 7上使用FileChannel.map读取数据后是否有重命名文件的解决方法,因为现在每个人似乎都有Java 7?
我试图了解以下行为。我的旧代码,
String path = "C:/temp/sample.txt";
String mode= "rw";
FileChannel channel = new RandomAccessFile(path, mode).getChannel();
// some code to write to this file
// finally delete
File file = new File(path);
boolean isDeleted = file.delete();
System.out.println("Is Deleted - " + isDeleted);
Run Code Online (Sandbox Code Playgroud)
O / P-已删除-否
仅当我执行“ channel.close();”时 在我删除文件之前。它是否删除文件并返回true。
较新的替换代码,
String path = "C:/temp/sample.txt";
FileChannel fChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
// some code to write to this file
// finally delete
File file = new File(path);
boolean isDeleted = file.delete();
System.out.println("Is Deleted …Run Code Online (Sandbox Code Playgroud) 正如我发现的,底层操作系统调用 is copyToFile(),Libcore.os.read(fd, bytes, byteOffset, byteCount)whiletransferTo()基于内存映射文件:
MemoryBlock.mmap(fd, alignment, size + offset, mapMode);
...
buffer = map(MapMode.READ_ONLY, position, count);
return target.write(buffer);
Run Code Online (Sandbox Code Playgroud)
Q1:我的发现是对还是错?
Q2:有什么理由使用FileUtils.copyFile()asFileChannel.transferTo()似乎应该更有效吗?
谢谢
这个问题困扰我好几天了:
假设我有两个在同一台计算机上运行的进程(p_write和p_read)。
过程p_write用于写入/更新mmap文件。
过程p_read是使用mmap文件的过程,换句话说,就是读取mmap文件。
我的假设是,p_write首先需要为mmap文件分配一个内存空间(堆外)(该空间使用Java MappedByteBufferAPI 自动映射到文件)。
我的问题是p_read如何从mmap文件读取?我现在的假设是,p_read还需要为要映射的mmap文件分配另一个相同大小的堆外空间,但这似乎是不正确的,因为在这种情况下,内存量需要加倍。
如果p_read不需要为要映射的mmap文件分配单独的内存空间,那么如何p_read知道文件映射到的正确内存地址p_write?
更新1
我发现有一个更好的问题要问,或者您可以将其视为后续问题:如果FileChannel.map()两次调用,同一文件是否将两次映射到两个不同的内存空间?
// Scenario A: In single process
try (FileChannel fc = FileChannel.open(filePath, openOptions)) {
// First call
fc.map(MapMode.READ_ONLY, 0, SIZE_CONSTANT);
// Second call
fc.map(MapMode.READ_ONLY, 0, SIZE_CONSTANT);
}
Run Code Online (Sandbox Code Playgroud)
和
// Scenario B: In two processes
// in first process
try (FileChannel fc = FileChannel.open(filePath, openOptions)) {
// First …Run Code Online (Sandbox Code Playgroud) java memory filechannel memory-mapped-files mappedbytebuffer
filechannel ×10
java ×10
nio ×5
bytebuffer ×2
file-io ×2
java-7 ×2
android ×1
bytearray ×1
file-copying ×1
fileutils ×1
gzip ×1
io ×1
java-io ×1
memory ×1
zip ×1