1

我想要一个对象来模拟(在纯 Java 中)大于整个虚拟内存(以 Tera 字节测量)的文件的内存映射。

/** interface to memory-mapped file emulator that can read/write to mapped file
 * and return multiple views on it. */
public interface FileView {

    /** returns single byte of mapped file at position (long index) */
    byte getByte (long pos);

    /** sets single byte of mapped file at position (long index) to a value */
    void setByte (long pos, byte value);

    /** returns ByteBuffer-like class exposing bytes of mapped file
     * in specified range (from - to, long indices).
     * Length of that buffer can be arbitrary limited (perhaps to 50MB) */
    XxxByteBuffer getBuffer (long from, long to);

    /** register new range of bytes that needs to be listened
     * and in case of change, listener should get informed.
     * RangeChangedListener takes two arguments in constructor: long from, long to */
    void addRangeChangedListener(RangeChangedListener l);
}

我的问题(6)在大标题下方,这里是上下文信息。

每个返回的类似 ByteBufer 的类的实例应该彼此保持一致(在重叠的区域中具有相同的内容)。因此,如果其中一个缓冲区发生更改,其他缓冲区如果重叠,则应适当更改。此外,如果更改发生在侦听区域,则应通知侦听器(对于缓冲区,同步和侦听应该是单一机制。对于代理视图 - 同步没有问题,但侦听器问题仍然存在)。这种同步不必是“超级高性能”,但它应该是可靠的。我认为会有很多重叠的缓冲区,但不会有很多修改。我假设大多数时候我会从“FileView”中获得很少的缓冲区(比如说 2kb)。

每个单独的写入都应该以某种方式存储在内存中(除了写入缓冲区)。在用户单击“保存”之前,不允许直接写入文件。当然它不能持续很长时间——我假设没有必要在内存中缓存超过 200MB 的内存(可以在磁盘上的临时文件中缓存大量的更改)。这些单独的更改可以分布在整个映射文件中。如果文件中的相邻位置被修改,则可以聚合更改。

所以 FileView 必须:

  1. 缓冲内存中当前使用的数据。
  2. 将所有单独的更改(写入)存储在缓存中。
  3. 按需公开类似 ByteBuffer 的对象(通过从文件中读取新数据或从内存中获取现有数据)并对其应用适当的缓存更改(如果存在任何更改)。
  4. 当它们重叠时,将类似 ByteBuffer 的对象相互同步。
  5. 从内存中丢弃干净的数据(当前未使用且未更改)(以保留内存)
  6. 在当前不使用时安全地从内存中丢弃“脏数据”:要做到这一点,必须确保存储更改,然后像干净一样丢弃数据
  7. 监控当前使用的内存量与最大值相比,在内存不足的情况下 - 尝试尽可能多地丢弃。

有两个大的性能问题:

1. 获取新数据作为视图,而不是作为独立缓冲区

在以下调用之后:

fileView.getBuffer(0,200000000);
fileView.getBuffer(0,200000789);

我们有两个 200MB 数据块的独立副本。连续调用将产生相同数据的下一个副本。所以也许 fileView 应该返回现有内部缓冲区而不是新缓冲区的视图?但是然后 - 如何产生内部缓冲区?这将是“拼凑”。fileView 将根据需要在内部创建缓冲区,并返回以某种方式粘合和修补在一起的这些内部缓冲区的视图。看下面 - 现有的内部缓冲区在第四次调用中被重用(修补):

fileView.getBuffer(0,2);  // {0,1,2}
fileView.getBuffer(4,5);  // {4,5}
fileView.getBuffer(8,12); // {8,9,10,11,12}
fileView.getBuffer(0,16); // {0,1,2} {3} {4,5} {6,7} {8,9,10,11,12} {13,14,15,16}

2. 在不破坏视图的情况下摆脱内部数据(GC 它们)

当内存不足时,我们应该:

  • GC 未更改的缓冲区(因为我们稍后可以再次从 HD 中读取它们);
  • 将我们的更改(写入)存储在缓存和 GC 修改的缓冲区中。

这样我们就可以完全摆脱任何读取数据(但将我们的更改(写入)保存在单独的缓存中)。但...

有缓冲区视图返回给外部组件 - 这些视图引用了我们的内部缓冲区,因此我们不能 GC 内部缓冲区。而且我们不想破坏这些外部视图——它们应该保持原样——我们只想摆脱内部数据,然后按需填充它们(如代理)。

.

我正在寻找一种架构上良好的解决方案来解决上述情况。这正是我需要知道的:

.

  1. 如何获取新数据作为视图而不是独立缓冲区?我想要:

    • 避免多个缓冲区中相同数据的多个独立副本,所以我想要视图
    • 在 fileView.getBuffer (long from, long to) 之后;返回功能类似于 ByteBuffer 的东西
    • 与裸 ByteBuffers 相比,不要过多地破坏性能也许我应该返回在 fileView 的内部缓冲区上实现代理视图并具有类似于 bytebuffer 的接口的全新类的实例?
  2. 什么数据结构适合跟踪已加载和未加载的内容?也许 TreeMap 加上叶子的 LinkedList 并在叶子内保留缓冲区?

  3. 如何在不破坏外部视图的情况下摆脱内部数据(GC)?

  4. 如何同步(=保证相等)重叠区域中返回的缓冲区/视图的内容?这个问题在视图的情况下消失了,因为每个视图都引用相同的底层结构。

  5. 如何有效地存储单个更改(写入),然后在从文件重新加载数据时将它们重新应用到新返回的缓冲区/视图?每一次更改都可以是一个字节长,但也可以是几兆字节长。

  6. 如何有效地将选定的字节范围映射到适当的侦听器(单个侦听器侦听定义的字节范围的变化——而不是整个缓冲区)?

.

对于那些仍在阅读的人,我可以提供一些关于我在做什么的额外细节:)

我对缓存几乎一无所知,对缓冲区知之甚少,但是......

我尝试创建一个内部带有脚本引擎 (DSL) 和 GUI-to-data binding 的十六进制编辑器。目的是数据分析和恢复。我选择的语言是 Java + Groovy。项目的某些部分现在处于后期阶段,它们基于 ByteBuffers、CharBuffers 等。

我的应用程序有以下业务限制

  1. 该应用程序可以打开超大文件(高清备份文件) - 比如说 1-2TB(Tera Bytes);
  2. 用户可以看到打开的文件,滚动并修改(就像在任何十六进制编辑器中一样);
  3. 脚本引擎可以随机访问文件并对其进行处理(读/写/绑定);
  4. 数据可以通过脚本绑定到 GUI,即字节 100000045 到 100000048 可以被视为 INTEGER 并显示在 JTextField 中。当这些字节发生变化时 - JTextField 也会发生变化。当有人更改文本字段时 - 基础数据(在文件中)会更改。
  5. 文件应该保持不变,直到我们点击保存。

脚本将严格在打开文件的上下文中运行,其主要任务是 (a) 读取和写入该文件 (b) 打印有关该文件的信息 (c) 将文件区域绑定到 GUI 中的字段。

我怀疑会有两种需求(脚本类型但有些模糊):

绝对脚本。可能需要整个文件和绝对寻址的脚本——它们可以寻找一些东西,即查找分区表并检查分区。我认为他们不会按顺序处理文件(如流)。相反,他们应该需要访问分散在整个文件中的几个地方,但这些地方不会很大——我怀疑大约 50MB。

相对脚本。通常在本地工作的脚本 - 它们可以将某些字节((由用户直接用鼠标或其他脚本指向))解释为具有已知布局的数据记录,并在 GUI 中动态创建字段并将数据绑定到该字段。

有过多的文件类型具有部分静态布局和一些磁盘数据结构。例如 NTFS 引导扇区在其开始附近具有静态布局:

char[8] - "File system ID"
uint16 - "Bytes per sector"
uint8 - "Sectors per cluster"
uint16 - "Reserved sectors"
...

“静态”是指您可以从文件读取字节到字节缓冲区,然后盲目地将前 8 个字节解释为描述文件系统 ID 的字符,然后将接下来的两个字节解释为无符号整数,指示每个扇区值的字节数,依此类推。所以你可以创建文本字段,用标签描述它们并在里面显示数据。

4

0 回答 0