2

我有一个非常大的双精度数组,我使用基于磁盘的文件和 MappedByteBuffers 的分页列表来处理,有关更多背景信息,请参阅此问题。我正在使用 Java 1.5 在 Windows XP 上运行。

这是我的代码的关键部分,它针对文件分配缓冲区...

try 
{
 // create a random access file and size it so it can hold all our data = the extent x the size of a double
 f = new File(_base_filename);
 _filename = f.getAbsolutePath();
 _ioFile = new RandomAccessFile(f, "rw");
 _ioFile.setLength(_extent * BLOCK_SIZE);
    _ioChannel = _ioFile.getChannel();

    // make enough MappedByteBuffers to handle the whole lot
 _pagesize = bytes_extent;
 long pages = 1;
 long diff = 0;
 while (_pagesize > MAX_PAGE_SIZE)
 {
  _pagesize  /= PAGE_DIVISION;
  pages *= PAGE_DIVISION;

  // make sure we are at double boundaries.  We cannot have a double spanning pages
  diff = _pagesize  % BLOCK_SIZE;
  if (diff != 0) _pagesize  -= diff;

 }

 // what is the difference between the total bytes associated with all the pages and the
 // total overall bytes?  There is a good chance we'll have a few left over because of the
 // rounding down that happens when the page size is halved
 diff = bytes_extent - (_pagesize  * pages);
 if (diff > 0)
 {
  // check whether adding on the remainder to the last page will tip it over the max size
  // if not then we just need to allocate the remainder to the final page
  if (_pagesize  + diff > MAX_PAGE_SIZE)
  {
   // need one more page
   pages++;
  }
 }

 // make the byte buffers and put them on the list
 int size = (int) _pagesize ;  // safe cast because of the loop which drops maxsize below Integer.MAX_INT
 int offset = 0;
 for (int page = 0; page < pages; page++)
 {
  offset = (int) (page * _pagesize );

  // the last page should be just big enough to accommodate any left over odd bytes
  if ((bytes_extent - offset) < _pagesize )
  {
   size = (int) (bytes_extent - offset);
  }

  // map the buffer to the right place 
     MappedByteBuffer buf = _ioChannel.map(FileChannel.MapMode.READ_WRITE, offset, size);

     // stick the buffer on the list
     _bufs.add(buf);
 }

 Controller.g_Logger.info("Created memory map file :" + _filename);
 Controller.g_Logger.info("Using " + _bufs.size() + " MappedByteBuffers");
    _ioChannel.close();
    _ioFile.close(); 
} 
catch (Exception e) 
{
 Controller.g_Logger.error("Error opening memory map file: " + _base_filename);
 Controller.g_Logger.error("Error creating memory map file: " + e.getMessage());
 e.printStackTrace();
 Clear();
    if (_ioChannel != null) _ioChannel.close();
    if (_ioFile != null) _ioFile.close();
 if (f != null) f.delete();
 throw e;
} 

分配第二个或第三个缓冲区后,出现标题中提到的错误。

我认为这与可用的连续内存有关,因此尝试了不同大小和页数的方法,但总体上没有好处。

“没有足够的存储空间来处理这个命令”到底是什么意思,如果有的话,我能做些什么呢?

我认为 MappedByteBuffers 的重点是能够处理比堆上容纳的更大的结构,并将它们视为在内存中的能力。

有什么线索吗?

编辑:

为了回应下面的答案(@adsk),我更改了我的代码,因此我在任何时候都不会有超过一个活动的 MappedByteBuffer。当我引用当前未映射的文件区域时,我会丢弃现有地图并创建一个新地图。经过大约 3 次地图操作后,我仍然遇到同样的错误。

GC 引用的错误没有收集 MappedByteBuffers 似乎仍然是 JDK 1.5 中的一个问题。

4

2 回答 2

3

我认为 MappedByteBuffers 的重点是能够处理比堆上容纳的更大的结构,并将它们视为在内存中的能力。

不。这个想法是/是允许您处理超过 2**31 个双精度数...假设您有足够的内存,并且使用的是 64 位 JVM。

(我假设这是这个问题的后续问题。)

编辑:显然,需要更多解释。

有许多限制在起作用。

  1. Java 有一个基本限制,即length数组的属性和数组索引的类型int。再加上有int符号和数组不能有负大小的事实,这意味着最大可能的数组可以包含2**31元素。此限制适用于 32 位和 64 位 JVM。它是 Java 语言的基本组成部分……就像char值从065535.

  2. 2**32使用 32 位 JVM会为 JVM 可寻址的字节数设置一个(理论上的)上限。这包括整个堆、您的代码和您使用的库类、JVM 的本机代码核心、用于映射缓冲区的内存……一切。(事实上​​,根据您的平台,如果地址空间,操作系统可能会为您提供比字节少得多的内容。)2**32

  3. 您在 java 命令行中提供的参数决定了 JVM 将允许您的应用程序使用多少堆内存。映射到使用MappedByteBuffer对象的内存不计算在内。

  4. 操作系统将为您提供的内存量取决于(在 Linux/UNIX 上)配置的交换空间总量、“进程”限制等。类似的限制可能适用于 Windows。当然,如果主机操作系统支持 64 位,并且您使用的是支持 64 位的硬件,则只能运行 64 位 JVM。(如果你有奔腾,那你就很不走运了。)

  5. 最后,系统中的物理内存量开始发挥作用。理论上,您可以要求您的 JVM 使用比您机器的物理内存大很多倍的堆等。在实践中,这是一个坏主意。如果您过度分配虚拟内存,您的系统将崩溃,应用程序性能将下降。

外卖是这样的:

  • 如果您使用 32 位 JVM,您可能会受限于可寻址内存的字节数2**31和字节数。无论您使用数组还是映射缓冲区,2**32这对于最大的介于2**29和双精度之间的空间都是足够的。2**30

  • 如果使用 64 位 JVM,则可以表示单个双精度数组2**31。映射缓冲区的理论限制将是2**63字节或2**61双倍,但实际限制将大致是您的机器拥有的物理内存量。

于 2009-12-18T09:08:27.203 回答
1

内存映射文件时,可能会在 32 位 VM 中耗尽地址空间。即使文件以小块映射并且这些 ByteBuffers 不再可访问,也会发生这种情况。原因是 GC 从不开始释放缓冲区。

请参阅http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6417205上的错误

于 2009-12-18T09:12:55.557 回答