分享

执行java.io.ByteArrayOutputStream.write 方法内存突然加大的原因与解决办法

 清明幻境 2018-04-08

问题描述:

在项目中用有个上传功能,会把文件转换成inputstream然后转换成byte[] 数组

转换代码如下:

[java] view plain copy
  1. public static byte[] convertstream(InputStream inStream) throws IOException {  
  2.         ByteArrayOutputStream swapStream = new ByteArrayOutputStream();  
  3.         byte[] buff = new byte[10240];  
  4.         int rc = 0;  
  5.         while ((rc = inStream.read(buff, 010240)) > 0) {  
  6.             swapStream.write(buff, 0, rc);  
  7.         }  
  8.         byte[] buf = swapStream.toByteArray();  
  9.         swapStream.close();  
  10.         return buf;  
  11.     }  

根据测试,上传一个7M的文件, 但是执行swapStream.write(buff, 0, rc); 这段代码的时候,一共消耗了20M的内存,不应该啊,内存多么重要的东西。


问题原因:

查看 ByteArrayOutputStream 的API得知 

此类实现了一个输出流,其中的数据被写入一个字节数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 检索数据。 

得知一个关键信息,就是缓冲区,查看ByteArrayOutputStream 源代码如下:

[java] view plain copy
  1. /** 
  2.      * Creates a new byte array output stream. The buffer capacity is  
  3.      * initially 32 bytes, though its size increases if necessary.  
  4.      */  
  5.     public ByteArrayOutputStream() {  
  6.     this(32);  
  7.     }  

我使用ByteArrayOutputStream的时候并没有在构造函数制定缓冲区的大小,所以默认是初始化32bytes,但是对于7M大的文件来说,32bytes 远远不够,所以

ByteArrayOutputStream 缓冲区会自动增长,这个自动增长的过程,根据源码来看会有copyArray动作,所以整个write过程,消耗内存成倍增长。

源码如下:

[java] view plain copy
  1. public synchronized void write(byte b[], int off, int len) {  
  2.     if ((off < 0) || (off > b.length) || (len < 0) ||  
  3.             ((off + len) > b.length) || ((off + len) < 0)) {  
  4.         throw new IndexOutOfBoundsException();  
  5.     } else if (len == 0) {  
  6.         return;  
  7.     }  
  8.         int newcount = count + len;  
  9.         if (newcount > buf.length) {  
  10.             buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));  
  11.         }  
  12.         System.arraycopy(b, off, buf, count, len);  
  13.         count = newcount;  
  14.     }  

解决办法:

我是这么解决的,不知道还有没有更好的办法。

我们可以得到上传文件File对象的length,在创建 ByteArrayOutputStream 对象的时候把length属性放在构造函数里面:

[java] view plain copy
  1. public static byte[] convertstream(InputStream inStream, long length) throws IOException {  
  2.         length = length > Integer.MAX_VALUE ? Integer.MAX_VALUE : length;  
  3.         ByteArrayOutputStream swapStream = new ByteArrayOutputStream((int)length);  
  4.         byte[] buff = new byte[10240];  
  5.         int rc = 0;  
  6.         while ((rc = inStream.read(buff, 010240)) > 0) {  
  7.             swapStream.write(buff, 0, rc);  
  8.         }  
  9.         byte[] buf = swapStream.toByteArray();  
  10.         swapStream.close();  
  11.         return buf;  
  12.     }  
最后测试结果 
[java] view plain copy
  1. swapStream.write(buff, 0, rc); 一共消耗7M内存  
若有更好的解决方案,请告知。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多