草庐IT

java - 在内存有限的系统上写入大文件时,如何避免 mapFailed() 错误

coder 2024-03-13 原文

我刚刚在我的 opensrc 库代码中遇到了一个错误,该错误分配了一个大缓冲区来修改一个大的 flac 文件,该错误只发生在使用 Java 1.8.0_74 25.74-b02 32bit 的具有 3Gb 内存的旧 PC 机器上

原来我只是分配一个缓冲区

ByteBuffer audioData = ByteBuffer.allocateDirect((int)(fc.size() - fc.position()));

但有一段时间我把它作为
MappedByteBuffer mappedFile = fc.map(MapMode.READ_WRITE, 0, totalTargetSize);

我的(错误)理解是映射缓冲区使用的内存比直接缓冲区少,因为整个映射缓冲区不必同时仅在使用的部分在内存中。但是这个答案说使用映射的字节缓冲区是一个坏主意,所以我不知道它是如何工作的

Java Large File Upload throws java.io.IOException: Map failed

完整代码可以在 here 看到

最佳答案

尽管映射缓冲区在任何时间点都可能使用较少的物理内存,但它仍然需要一个等于缓冲区总(逻辑)大小的可用(逻辑)地址空间。更糟糕的是,它可能(可能)要求地址空间是连续的。无论出于何种原因,那台旧计算机似乎无法提供足够的额外逻辑地址空间。两种可能的解释是 (1) 有限的逻辑地址空间 + 大量缓冲内存要求,以及 (2) 操作系统对可映射为 I/O 文件的内存量施加的一些内部限制。

关于第一种可能性,请考虑这样一个事实,即在虚拟内存系统中,每个进程都在其自己的逻辑地址空间中执行(因此可以访问完整的 2^32 字节寻址空间)。因此,如果--在您尝试实例化 MappedByteBuffer 的时间点--JVM 进程的当前大小加上 MappedByteBuffer 的总(逻辑)大小|大于 2^32 字节(~ 4 GB),那么你会遇到 OutOfMemoryError (或该类选择抛出的任何错误/异常,例如 IOException: Map failed )。

关于第二种可能性,评估这一点的最简单方法可能是在您尝试实例化 MappedByteBuffer 时分析您的程序/JVM。 .如果JVM进程分配的内存+所需的totalTargetSize远低于 2^32 字节上限,但您仍然收到“映射失败”错误,那么可能是操作系统对内存映射文件大小的某些内部限制是根本原因。

那么这意味着什么尽可能的解决方案呢?

  • 只是不要使用那台旧电脑。 (最好,但可能不可行)
  • 确保 JVM 中的其他所有内容在 MappedByteBuffer 的生命周期内都具有尽可能低的内存占用。 . (看似合理,但可能不相关且绝对不切实际)
  • 将该文件分解成更小的块,然后一次只对一个块进行操作。 (可能取决于文件的性质)
  • 使用不同的/较小的缓冲区。 ...并忍受性能下降。 (这是最现实的解决方案,即使它最令人沮丧)


  • 另外,totalTargetSize究竟是什么?对于您的问题案例?

    编辑:

    经过一番挖掘,似乎很明显 IOException 是由于 running out of address space in a 32-bit environment .即使由于缺乏足够的连续地址空间,或者由于 JVM 中其他足够大的地址空间要求同时结合大 MappedByteBuffer,即使文件本身小于 2^32 字节,也会发生这种情况。请求 ( see comments )。需要明确的是,仍然可以抛出 IOE 而不是 OOM even if the original cause is ENOMEM .此外,特别是旧的 [在此处插入 Microsoft 操作系统] 32 位环境( exampleexample )似乎存在问题。

    所以看起来你有三个主要选择。
  • 一起使用“the 64-bit JRE or...another operating system”。
  • 使用不同类型的较小缓冲区并对文件进行分块操作。 (并因不使用映射缓冲区而降低性能)
  • 继续使用 MappedFileBuffer出于性能原因,但也以较小的块对文件进行操作,以解决地址空间限制。

  • 我使用 MappedFileBuffer 的原因在较小的块中作为第三个是因为在取消映射 MappedFileBuffer 时存在完善且 Unresolved 问题。 ( example ),这是您在处理每个块之间必须要做的事情,以避免由于累积映射的组合地址空间占用而达到 32 位上限。 (注意:这仅适用于 32 位地址空间上限而不是一些内部操作系统限制的问题......如果是后者,则忽略此段)您可以尝试 this strategy (删除所有引用,然后运行 ​​GC),但您基本上会受制于 GC 和您的底层操作系统如何在内存映射文件方面进行交互。其他试图或多或少直接操作底层内存映射文件 ( example ) 的潜在解决方法非常危险,并且受到 Oracle ( see last paragraph ) 的特别谴责。最后,考虑到 GC 行为无论如何都是不可靠的,而且官方文档明确指出“many of the details of memory-mapped files [are] unspecified”,我会不是 推荐使用 MappedFileBuffer无论您可能读到什么解决方法,都像这样。

    因此,除非您愿意冒险,否则我建议您要么遵循 Oracle 的明确建议(第 1 点),要么将文件处理为使用不同缓冲区类型的一系列较小块(第 2 点)。

    关于java - 在内存有限的系统上写入大文件时,如何避免 mapFailed() 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40545683/

    有关java - 在内存有限的系统上写入大文件时,如何避免 mapFailed() 错误的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    4. ruby - 其他文件中的 Rake 任务 - 2

      我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

    5. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

      我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

    6. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    7. ruby-on-rails - Rails 3 中的多个路由文件 - 2

      Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

    8. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

      给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

    9. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    10. ruby - 将差异补丁应用于字符串/文件 - 2

      对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

    随机推荐