我编写了一个转换器,它接受 openstreetmap xml 文件并将它们转换为二进制运行时渲染格式,该格式通常约为原始大小的 10%。输入文件大小通常为 3GB 或更大。输入文件不会一次全部加载到内存中,而是以收集点和多边形的方式进行流式传输,然后对它们运行 bsp 并输出文件。最近,在较大的文件上,它会耗尽内存并死掉(有问题的文件有 1400 万个点和 100 万个多边形)。通常,当发生这种情况时,我的程序会使用大约 1GB 到 1.2GB 的 RAM。我尝试将虚拟内存从 2 GB 增加到 8 GB(在 XP 上),但此更改没有效果。另外,由于此代码是开源的,我希望它可以在 Windows、Linux 和 Mac 上运行,而不管可用的 ram(尽管速度较慢)如何。

我可以使用哪些技术来避免内存不足?以较小的子集处理数据,然后合并最终结果?使用我自己的虚拟内存类型的处理程序?还有其他想法吗?

有帮助吗?

解决方案

首先,一个32位的系统上,你会总是限于4 GB的存储器,无论页面文件的设置。 (而这些,只有2GB将提供给您的Windows进程。在Linux上,您通常有大约3GB可用)

因此,第一个明显的解决方案是切换到一个64位操作系统,并编译为64位应用程序。这就给了你一个巨大的虚拟内存空间使用,操作系统将进出必要的页面文件,以使事情工作的交换数据。

其次,在时间可以帮助分配存储器较小的块。它往往更容易找到的自由存储器4 256MB块比一个1GB块。

第三,分裂的问题。不同时处理的整个数据集,但尝试一次加载和过程仅一小部分。

其他提示

您是否检查过以确保没有在任何地方泄漏内存?

由于您的程序可移植到 Linux,因此我建议在 Valgrind 下运行它以进行确认。

这听起来像你已经做了 SAX 基础的方法来处理XML(加载XML,你全力以赴而不是一次)。

的解决方案是几乎总是改变算法,使得它减少这个问题成较小的部分。身体上没有一次分配尽可能多的内存,读取你只需要什么,过程,然后写出来。

可以通过有时需要在算法时使用的硬盘驱动器,而不是延伸存储器。

如果您不能分割你的算法,你可能想是这样的存储器映射文件

在最坏的情况下,你可以尝试使用类似的虚拟如果你是一个Windows系统上。如果你是一个32位系统上,你可以尝试使用类似的物理地址扩展(PAE)

您也可以考虑把输入限制为您的程序,并具有不同的一个32位和64位系统。

我怀疑你的记忆问题是从保持BSP树在内存中。因此,保持了BSP在磁盘上,只保留一些块在内存中。这应该是与BSP相当容易,因为结构使其本身比一些其他的树结构,逻辑应该很简单。既高效和内存友好你可以有一个缓存W /脏标志,设置为可用内存高速缓存的大小有点少了喘息的空间。

假设你使用的是Windows XP,如果你仅仅只过了你的内存限制,不希望或需要返工代码的时间为上述建议,您可以将/ 3GB开关添加到您的 BOOT.INI 文件,然后它只是一个设置链接器开关的事情来获得额外的1GB的存储器中。

您要明白,虚拟内存是从“RAM”不同之处在于你使用的虚拟内存量是你一直保留的总金额,而实际内存(在Windows其所谓的工作集)是内存你实际上已经修改或锁定。

至于别人指出的那样,在32位Windows平台上的虚拟内存的限制为2千兆字节,除非你设置特殊标志3千兆字节,可以确保无论是在你的代码和任何库只使用全部使用指针未签名的指针。

所以,或者强迫用户为64位或监视您的虚拟内存和封盖你的最大块大小的东西,通过舒适的32位操作系统征收将是我的建议的范围内配合。

我已经撞上在Windows 32位的墙,但与周围Linux的这些限制工作,所以我只谈过的事情Windows方面没有经验。

在32位XP最高程序地址空间为2GB。然后,你必须碎片由于DLL的程序和驱动程序到您的地址空间加载了。最后,你有你的堆碎片化的问题。

您最好的举动只是为了结束它并运行一个64位进程(64位系统)。突然,所有这些问题消失。您可以使用更好的堆,以减轻堆碎片效果,你可以尝试使用的VirtualAlloc抓住你的内存在一个大的连续的块(然后你从那里管理它!)从分段来劝阻的DLL /驱动器。

最后,你可以在流程拆分您的BSP。复杂而痛苦的,坦率地说只是把它在磁盘上会更容易些,但在理论上,你可以通过具有群交流信息,如果你可以把一切都驻留程序(并假设您可以用比内存比操作系统更聪明获得更好的性能可以处理文件缓冲......这是个很大的可能)。每个进程都需要少得多的内存中,因此不应该运行到2GB的地址空间限制。当然,你会通过RAM刻录/交换速度快了很多。

可以通过分配较小的块减轻的地址空间碎片的影响。这将有其他讨厌的副作用,但你可以遵循,你如果你无法成功抢分配的内存更小,更小的块退避策略。通常这种简单的方法将让你的程序工作时,否则不会,但是剩下的时间里执行,以及它可能。

男孩,并不64位计算只是声音如此好得多比其他选择?

你是如何为点分配内存?你在一个时间点分配一个(例如 PT =新点)。然后,根据点的大小,一些内存可能会浪费了。例如在Windows上分配内存在16个字节的倍数,所以即使你问尝试分配1个字节,OS实际上将分配16个字节。

如果是这种情况,使用存储器分配器可以帮助。您可以使用STL分配器进行快速检查。 (超过加载Point类新的运营商和使用STL分配器来分配存储器,而不是“的malloc”或默认的新操作符)。

您可以不被分配和以最佳的方式释放内存。正如其他人所指出的那样,你可能会泄漏内存,不知道它。调试和优化内存分配需要时间。

如果你不想花时间优化内存使用情况,为什么不尝试的保守的垃圾收集器?这是一个插件替代的malloc()/新和free()。事实上,免费的()是一个空操作,所以你可以删除你的程序这些调用。相反,如果你手工优化你的程序和管理的内存池,如先前建议,你最终做了很多的CGC已经为你做的工作。

您需要将您的流输出和输入。如果您的输出格式不面向流的,可考虑做第二遍。例如,如果输出文件与校验和开始/数据的大小,留在第一次通过空间和寻求/写入到该空间后。

这听起来像你正在做的txt二进制谈话,为什么你需要在内存中的全部数据?结果 你就不能读TXT(XML)的原始然后保存到binarystream?

如果你想成为独立的内存大小,你需要一个尺寸无关的算法。无论您的RAM是什么尺寸,如果你没有得到控制内存的使用,你会碰到边框。

看看的你都不可能使用,以产生一个位输出的信息的至少块。然后想办法来划分输入到该尺寸的块。

现在听起来很容易,不是吗? (GLAD我没有做到这一点:))

您不需要切换到 64 位机器,也不需要其他人建议的 1000 件事中的大部分。你需要的是一个更周到的算法。

您可以采取以下一些措施来帮助解决这种情况:

  • 如果您使用的是 Windows,请使用文件映射 (示例代码)。这将通过单个缓冲区指针来访问该文件,就像您在内存中读取整个文件一样,但实际上并没有这样做。最近版本的 Linux 内核也有类似的机制。
  • 如果可以的话,而且看起来也可以,请按顺序扫描文件并避免创建内存中 DOM。这将大大减少您的加载时间以及内存需求。
  • 使用池内存!您可能会有许多微小的对象,例如节点、点等等。使用池内存来帮助解决问题(我假设您使用的是非托管语言。搜索池化分配和内存池)。
  • 如果您使用的是托管语言,至少将此特定部分移至非托管语言并控制内存和文件读取。托管语言在内存占用和性能方面都有不小的开销。(是的,我知道它被标记为“C++”...)
  • 尝试设计一种就地算法,一次仅读取和处理最少量的数据,这样您的内存需求就会降低。

最后,我要指出,复杂的任务需要复杂的措施。如果你认为你能买得起带有 8GB RAM 的 64 位机器,那么只需使用“将文件读入内存,处理数据,写入输出”算法,即使需要一天才能完成。

有对于一个很好的技术,在一定情况下,保存到文件,并让他们以后当你需要使用它们。

此技术被用于许多的开源软件等的Doxygen需要的存储器的数量大时,可扩展的。

这是一个老问题,但是,因为我最近做同样的事情....

有没有简单的答案。在一个理想的世界里,你会使用一台具有巨大的地址空间(即64位),与物理内存的海量。单独巨大的地址空间是不够的,或者只会鞭打。在这种情况下解析XML文件导入到数据库中,并以适当的查询,拔出你需要什么。很可能这是OSM本身并没有(我相信世界上大约330GB)。

在现实中,我仍然使用XP 32位的权宜之计的原因。

这是关闭空间和速度之间的折衷。你可以在内存中的任何数量相当多的东西提供你不在乎它需要多长时间。使用STL结构,你可以分析你想要的任何东西,但你很快就会运行内存不足。您可以定义自己的分配器:交换,但同样,这将是低效的,因为地图,矢量,台等真的不知道自己在做什么。

我发现,使之成为32位机上采用小尺寸的所有工作的唯一途径是非常仔细地约需要我在做什么,什么时候想,把任务分成块。内存高效(永远不会使用超过100MB〜),但没有大量的快,但随后也不要紧 - ?多久一个具有解析XML数据

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top