Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/amazon-web-services/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 试图处理s3文件时出现OOM_Java_Amazon Web Services_Amazon S3_Aws Lambda_Gzipinputstream - Fatal编程技术网

Java 试图处理s3文件时出现OOM

Java 试图处理s3文件时出现OOM,java,amazon-web-services,amazon-s3,aws-lambda,gzipinputstream,Java,Amazon Web Services,Amazon S3,Aws Lambda,Gzipinputstream,我正试图使用下面的代码从文件中下载和读取数据,无论这种情况如何,在读取文件时,s3文件的大小是22MB,我通过浏览器下载的是650MB,但当我通过visual VM进行监控时,解压缩和读取时消耗的内存超过2GB。任何人请引导,以便我会找到高内存使用的原因。谢谢 public static String unzip(InputStream in) throws IOException, CompressorException, ArchiveException { Syst

我正试图使用下面的代码从文件中下载和读取数据,无论这种情况如何,在读取文件时,s3文件的大小是22MB,我通过浏览器下载的是650MB,但当我通过visual VM进行监控时,解压缩和读取时消耗的内存超过2GB。任何人请引导,以便我会找到高内存使用的原因。谢谢

public static String unzip(InputStream in) throws IOException, CompressorException, ArchiveException {
            System.out.println("Unzipping.............");
            GZIPInputStream gzis = null;
            try {
                gzis = new GZIPInputStream(in);
                InputStreamReader reader = new InputStreamReader(gzis);
                BufferedReader br = new BufferedReader(reader);
                double mb = 0;
                String readed;
                int i=0;
                while ((readed = br.readLine()) != null) {
                     mb = mb+readed.getBytes().length / (1024*1024);
                     i++;
                     if(i%100==0) {System.out.println(mb);}
                }


            } catch (IOException e) {
                e.printStackTrace();
                LOG.error("Invoked AWSUtils getS3Content : json ", e);
            } finally {
                closeStreams(gzis, in);
            }
主线程java.lang.OutOfMemoryError中出现异常:java堆空间 java:3332 at java.lang.AbstractStringBuilder.ensureCapacityInternalAbstractStringBuilder.java:124 在 java.lang.AbstractStringBuilder.appendAbstractStringBuilder.java:596 在java.lang.StringBuffer.appendStringBuffer.java:367 at java.io.BufferedReader.readLineBufferedReader.java:370at java.io.BufferedReader.readLineBufferedReader.java:389 at com.kpmg.rrf.utils.AWSUtils.unzipAWSUtils.java:917


这是一个理论,但我想不出还有什么其他原因可以解释你的例子

假设未压缩文件包含一个很长的行;e、 大约6.5亿ASCII字节

您的应用程序似乎一次只读取一行文件,并尝试显示已读取的运行总兆字节数

在内部,readLine方法一次读取一个字符,并将它们附加到StringBuffer。您可以在堆栈跟踪中看到append调用。如果文件包含非常大的行,那么StringBuffer将变得非常大

未压缩字符串中的每个文本字符都将成为字符串缓冲区的缓冲区部分char[]中的一个字符

每次缓冲区填满时,StringBuffer都会将缓冲区的大小增加一倍。这需要分配一个新字符[]并将字符复制到其中

因此,如果缓冲区在有N个字符时填满,Arrays.copyOf将分配一个字符[]来容纳2 x N个字符。在复制数据时,将使用总共3 x N的字符存储

因此650MB很容易变成大于6 x 650M字节的堆需求

需要注意的另一点是,2xn数组必须是单个连续堆节点

看看堆图,看起来堆的使用量达到了~1GB。如果我的理论是正确的,那么下一次分配将针对~2GB节点。但是1GB+2GB正好是3.1GB堆最大值的限制。当我们考虑到连续性要求时,无法完成分配

那么解决方案是什么呢

这真的很简单:如果可能导致行的长度不合理,就不要使用readLine

    public static String unzip(InputStream in) 
            throws IOException, CompressorException, ArchiveException {
        System.out.println("Unzipping.............");
        try (
            GZIPInputStream gzis = new GZIPInputStream(in);
            InputStreamReader reader = new InputStreamReader(gzis);
            BufferedReader br = new BufferedReader(reader);
        ) {
            int ch;
            long i = 0;
            while ((ch = br.read()) >= 0) {
                 i++;
                 if (i % (100 * 1024 * 1024) == 0) {
                     System.out.println(i / (1024 * 1024));
                 }
            }
        } catch (IOException e) {
            e.printStackTrace();
            LOG.error("Invoked AWSUtils getS3Content : json ", e);
        }

我还想到了太长的队伍。 再想一想,我认为JVM内部使用的StringBuffer需要转换为readline的结果类型:String。字符串是不可变的,但出于速度原因,如果一行重复,JVM甚至不会进行查找。因此,它可能会多次分配字符串,最终用不再使用的字符串片段填充堆

我的建议不是读取行或字符,而是读取字节块。字节[]在堆上分配,之后可以丢弃。当然,您将计算字节而不是字符。除非你知道其中的区别,并且需要更稳定、更高效的解决方案

此代码仅由内存编写,未经测试:

public static String unzip(InputStream in) 
            throws IOException, CompressorException, ArchiveException {
        System.out.println("Unzipping.............");
        try (
            GZIPInputStream gzis = new GZIPInputStream(in);
        ) {
            byte[] buffer = new byte[8192];
            long i = 0;
            int read = gzis.read(buffer);
            while (read >= 0) {
                 i+=read;
                 if (i % (100 * 1024 * 1024) == 0) {
                     System.out.println(i / (1024 * 1024));
                 }
                 read = gzis.read(buffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
            LOG.error("Invoked AWSUtils getS3Content : json ", e);
        }```

请将您的问题包括您得到的实际异常,包括stacktrace。指出您发布的代码的哪一行引发了异常。您是说解压后的文件是650 MB,而您的VM在运行OOM之前使用了2 GB?谢谢Kenster,为问题添加了更多信息。@AndyMan JVM使用了2 GB以上,以交叉检查我通过浏览器从S3下载的文件,其中文件大小为22MB,下载后,磁盘上大约有650 MB,这是导致问题的真正代码吗?它似乎缺少了一些东西,即对您正在读取的数据执行某些操作的代码。您在这里发布的只是一些计算兆字节数的逻辑。总之,我需要整个字符串,而不仅仅是大小,因为我必须使用StringBuilder/Buffer,inturn将再次导致OOM这是正确的。你将不得不重新思考你这样做的方式。1不要使用readline。2不要将其存储为字符串。3如果您将StringBuilder预分配到足够大的大小以容纳未压缩的文本,则StringBuilder可能会工作。4您可以通过不转换为/存储为字符数据来节省空间。