当前位置: 首页 > 面试题库 >

java.lang.OutOfMemoryError:调用Files.readAllBytes时直接缓冲内存

麹权
2023-03-14
问题内容

我有以下代码,旨在读取目录并将其压缩到tar.gz归档文件中。当我将代码部署到服务器上并使用一批文件对其进行测试时,它可以在前几个测试批处理中使用,但是在第4批或第5批处理之后,它将始终如一地为我提供java.lang.OutOfMemoryError:即使直接缓冲内存文件批处理大小保持不变,并且堆空间看起来不错。这是代码:

public static void compressDirectory(String archiveDirectoryToCompress) throws IOException {
Path archiveToCompress = Files.createFile(Paths.get(archiveDirectoryToCompress + ".tar.gz"));

try (GzipCompressorOutputStream gzipCompressorOutputStream = new GzipCompressorOutputStream(
    Files.newOutputStream(archiveToCompress));
     TarArchiveOutputStream tarArchiveOutputStream = new TarArchiveOutputStream(gzipCompressorOutputStream)) {
  Path directory = Paths.get(archiveDirectoryToCompress);
  Files.walk(directory)
      .filter(path -> !Files.isDirectory(path))
      .forEach(path -> {
        String
            stringPath =
            path.toAbsolutePath().toString().replace(directory.toAbsolutePath().toString(), "")
                .replace(path.getFileName().toString(), "");
        TarArchiveEntry tarEntry = new TarArchiveEntry(stringPath + "/" + path.getFileName().toString());
        try {
          byte[] bytes = Files.readAllBytes(path); //It throws the error at this point.
          tarEntry.setSize(bytes.length);
          tarArchiveOutputStream.putArchiveEntry(tarEntry);
          tarArchiveOutputStream.write(bytes);
          tarArchiveOutputStream.closeArchiveEntry();
        } catch (Exception e) {
          LOGGER.error("There was an error while compressing the files", e);
        }
      });
}

}

这是一个例外:

Caused by: java.lang.OutOfMemoryError: Direct buffer memory
    at java.nio.Bits.reserveMemory(Bits.java:658)
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
    at sun.nio.ch.Util.getTemporaryDirectBuffer(Util.java:174)
    at sun.nio.ch.IOUtil.read(IOUtil.java:195)
    at sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:158)
    at sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:65)
    at sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:109)
    at sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:103)
    at java.nio.file.Files.read(Files.java:3105)
    at java.nio.file.Files.readAllBytes(Files.java:3158)
    at com.ubs.gfs.etd.reporting.otc.trsloader.service.file.GmiEodFileArchiverService.lambda$compressDirectory$4(GmiEodFileArchiverService.java:124)
    at com.ubs.gfs.etd.reporting.otc.trsloader.service.file.GmiEodFileArchiverService$$Lambda$19/183444013.accept(Unknown Source)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Iterator.forEachRemaining(Iterator.java:116)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
    at com.ubs.gfs.etd.reporting.otc.trsloader.service.file.GmiEodFileArchiverService.compressDirectory(GmiEodFileArchiverService.java:117)
    at com.ubs.gfs.etd.reporting.otc.trsloader.service.file.GmiEodFileArchiverService.archiveFiles(GmiEodFileArchiverService.java:66)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:113)
    at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:102)
    at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:49)
    at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:347)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:88)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:131)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:330)
    at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:166)
    at org.springframework.integration.util.MessagingMethodInvokerHelper.processInternal(MessagingMethodInvokerHelper.java:317)
    ... 93 more

我认为有一个缓冲区内存泄漏,因为它在前4个测试批次上都可以正常工作,但随后始终给出java.lang.OutOfMemoryError:之后出现直接缓冲区错误,但我不知道如何解决。我在这里看到了使用Cleaner方法的潜在解决方案:http :
//www.java67.com/2014/01/how-to-fix-javalangoufofmemoryerror-direct-byte-
buffer-java.html

但我不知道这种情况是否适用。

- - - - - - - - - - - - 编辑 - - - - - - - - - - - -

我找到了另一种有关如何使用IOUtils和缓冲的输入流来对文件进行tar处理的方法,并解决了该问题,即更新了代码:

  public static void compressDirectory(String archiveDirectoryToCompress) throws IOException {
Path archiveToCompress = Files.createFile(Paths.get(archiveDirectoryToCompress + ".tar.gz"));

try (GzipCompressorOutputStream gzipCompressorOutputStream = new GzipCompressorOutputStream(
    Files.newOutputStream(archiveToCompress));
     TarArchiveOutputStream tarArchiveOutputStream = new TarArchiveOutputStream(gzipCompressorOutputStream)) {
  Path directory = Paths.get(archiveDirectoryToCompress);
  Files.walk(directory)
      .filter(path -> !Files.isDirectory(path))
      .forEach(path -> {
        TarArchiveEntry tarEntry = new TarArchiveEntry(path.toFile(),path.getFileName().toString());
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(path.toString()))) {
          tarArchiveOutputStream.putArchiveEntry(tarEntry);
          IOUtils.copy(bufferedInputStream, tarArchiveOutputStream);
          tarArchiveOutputStream.closeArchiveEntry();
        } catch (Exception e) {
          LOGGER.error("There was an error while compressing the files", e);
        }
      });
}

}


问题答案:

实际上,只需调用即可获取文件大小file.length()。尝试更改从文件读取字节的方式:

tarArchiveOutputStream.write(IOUtils.toByteArray(new FileInputStream(path.toFile())));

来自apache commons IO包(http://commons.apache.org/proper/commons-
io/
)的IOUtils类。我认为这应该有助于解决您的麻烦。在某些情况下,建议使用@afretas。



 类似资料:
  • 问题内容: 在编写用于OpenGL库的Matrix类时,我遇到了一个问题,即使用Java数组还是使用Buffer策略存储数据(JOGL为Matrix操作提供直接缓冲区复制)。为了对此进行分析,我编写了一个小型性能测试程序,该程序比较了Arrays vs Buffers和Direct Buffers上循环和批量操作的相对速度。 我想在这里与您分享我的结果(因为我发现它们很有趣)。请随时发表评论和/或

  • 问题内容: 由于它不在jvm heap&gc中,何时发布?还是一直保留到流程终止? 但是所有答案都是模糊的,没有一个明确的答案,是否有明确的答案?至少适用于 64位Linux 上的 Java 8 。 __ 问题答案: 不使用旧的Java终结器。相反,它使用内部API。它创建一个新线程并存储到每个创建的线程中(除了重复和切片指的是主缓冲区)。当变成 幻影可到达的 (也就是说,不再存在对字节缓冲区的强

  • 问题内容: 我还没有使用过Redis,但我听说过它,并打算尝试将其作为缓存存储。 我听说Redis使用内存作为缓存存储数据库,那么如果我使用变量作为对象或字典数据类型来存储数据有什么区别?喜欢: Redis有什么优势? 问题答案: Redis是一个 远程 数据结构服务器。这肯定比仅将数据存储在本地内存中要慢(因为它涉及套接字往返来获取/存储数据)。但是,它也带来了一些有趣的属性: 应用程序的所有进

  • 问题内容: 我们正在构建具有积极性能SLA的Web应用程序,由于JVM由于System.gc()调用而无法正常工作,因此这些SLA经常遭到违反。我们已经进行了一些调试,并确定在所有情况下都是调用System.gc()的内部应用服务器代码。在应用服务器启动或部署应用程序时,这种情况会发生几次,我们并不在意。但是,当应用程序通过内部应用程序服务器启动和运行并调用NIO类时,也会定期触发System.g

  • 我们正在kubernetes上运行一个5节点flink集群(1.6.3),具有5个分区Kafka主题源。从该主题读取5个作业(具有不同的消费组),每个作业的并行度=5。 每个任务管理器都使用10Gb的ram运行,任务管理器堆大小限制为2Gb。摄取负载相当小(每秒100-200 msgs),平均消息大小约为4-8kb。所有作业都可以正常运行几个小时。经过一段时间,我们突然看到一个或多个作业失败: f

  • 问题内容: 假设我想使用标准模块从套接字读取一行: 到底发生了什么?每次都会发出系统调用吗?我想无论如何我应该添加一些缓冲: 为了与硬件和网络实际情况达到最佳匹配, bufsize 的值应为2的相对较小的幂,例如4096。 http://docs.python.org/library/socket.html#socket.socket.recv 但是编写高效且线程安全的缓冲似乎并不容易。如果我使用