如何修复 java.lang.OutOfMemoryError:Java 堆空间错误? [复制]

Posted

技术标签:

【中文标题】如何修复 java.lang.OutOfMemoryError:Java 堆空间错误? [复制]【英文标题】:How to fix java.lang.OutOfMemoryError: Java heap space error? [duplicate] 【发布时间】:2019-08-15 08:11:30 【问题描述】:

我有一个大小为 32 MB 的文件,我已将其从 DocuShare 服务器下载到 DocuShare 临时文件夹。我正在尝试从中读取文件内容以创建文件。对 base64 内容进行 URL 编码时出现错误。 当我在一个简单的 java 应用程序中运行相同的代码时,我没有遇到任何异常。但是当我在 DocuShare 服务中使用相同的代码来获取文档内容时,我得到了异常。 HTTP 状态 500 - org.glassfish.jersey.server.ContainerException:java.lang.OutOfMemoryError:Java 堆空间

org.glassfish.jersey.server.ContainerException: java.lang.OutOfMemoryError: Java 堆空间

File file = new File(filePath);
FileInputStream fileInputStreamReader = new FileInputStream(file);
byte[] bytes = new byte[(int)file.length()];
fileInputStreamReader.read(bytes);
String encodedBase64 = String encodedBase64 = java.util.Base64.getEncoder().encodeToString(bytes);
String urlEncoded = URLEncoder.encode(encodedBase64);

如何解决这个错误? 我需要增加我的 tomcat 堆大小吗?

【问题讨论】:

***.com/questions/1565388/increase-heap-size-in-java 首先您应该确定收到此错误时您的堆使用量实际是多少。您似乎正在存储该文件内容的多个副本。 (此外,您可能应该首先使用 URL 安全的 Base64 字符集。) 为JVM提供足够的内存或者优化你的代码。 【参考方案1】:

Base64 将每 3 个字节转换为 4 个字母。这意味着您可以分块读取数据并以与解码整个文件相同的方式对其进行解码。

试试这个:

       File file = new File(filePath);
       FileInputStream fileInputStreamReader = new FileInputStream(file);
       StringBuilder sb = new StringBuilder();
       Base64.Encoder encoder = java.util.Base64.getEncoder();
       int bufferSize = 3 * 1024; //3 mb is the size of a chunk
       byte[] bytes = new byte[bufferSize]; 
       int readSize = 0;

       while ((readSize = fileInputStreamReader.read(bytes)) == bufferSize) 
            sb.append(encoder.encodeToString(bytes));
       

       if (readSize > 0) 
            bytes = Arrays.copyOf(bytes, readSize);
            sb.append(encoder.encodeToString(bytes) );
       

       String encodedBase64  = sb.toString();

【讨论】:

【参考方案2】:

有两种方法可以解决此问题。

    您可以增加堆大小,但 IMO 这是一个糟糕的解决方案,因为如果您收到多个并行请求或尝试处理更大的文件时,您会遇到同样的问题。

    李>

    您可以优化您的算法 - 您可以以流式方式处理它,而不是在内存中存储多个文件副本,从而在内存中不超过几个 KB:

    import java.io.InputStream;
    import java.io.OutputStream;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.Base64;
    
    public class Launcher 
        public static void main(String[] args) throws Exception 
            final Path input = Paths.get("example");
            final Path output = Paths.get("output");
    
            try (InputStream in = Files.newInputStream(input); OutputStream out = Base64.getUrlEncoder().wrap(Files.newOutputStream(output))) 
                final byte[] buffer = new byte[1024 * 8];
    
                for (int read = in.read(buffer); read > 0; read = in.read(buffer)) 
                    out.write(buffer, 0, read);
                
            
        
    
    

PS:如果您真的需要 URL 编码器,则必须创建它的流式传输版本,但我认为 URL 安全的 base64 就足够了

【讨论】:

增加堆大小!诡异的!为什么你会建议一些你自己说的不好的解决方案?让我指出,这根本不是程序化的解决方案。您应该删除您的第 1 点。 @MohammadRakibAmin 我不推荐它——恰恰相反。我只是提到它作为一种可能的解决方案。 好答案。重要的是要提到增加堆大小,因为 OP 提到了它。增加 -Xmx 将使 OOM 错误在小范围内消失。此处的解决方案 2 是一个更好的答案,因为它降低了应用程序的峰值堆需求。 Jersey 在 servlet 容器中,将 32MB 文件作为单个块返回,最终也会为每个服务于大文件的请求线程分配一个大的非堆直接缓冲区。一些容器在完成请求后持有这些,直到请求线程被杀死,这可能永远不会。 @MohammadRakibAmin 这是一个选项。它可能(通常)不是一个好的选择,但它是一个选择。在某些情况下,这可能是必不可少的,例如,如果您必须在内存中保留固定数量的资源,而分配的总资源不足以容纳这些资源。 为了您的信息,您忽略了Duplicate 中的详细答案,使用了有限的存储空间。最重要的是,OP 询问他是否需要增加分配的堆大小。他没有问如何增加堆大小。否则我不会对着错误的树狂吠。在题为“如何修复”的问题中,仅关闭 OutOfMemory 错误不能成为编程解决方案。【参考方案3】:

如果您有大文件,您总是会遇到 OOM 错误,具体取决于文件的大小。如果您的目标是使用 Apache Commons Base64 Streams 进行 base64 编码。

https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/binary/Base64InputStream.html

【讨论】:

以上是关于如何修复 java.lang.OutOfMemoryError:Java 堆空间错误? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何修复 npm 审计修复问题?

如何修复drv?

如何修复漏洞

如何修复WMI

PHP网站漏洞怎么修复 如何修补网站程序代码漏洞

如何修复这些漏洞? (npm audit fix 无法修复这些漏洞)