ByteArrayOutputStream性能

时间:2017-08-08 09:32:57

标签: java performance inputstream apache-tika bytearrayoutputstream

我的要求是创建2个输入流副本,一个用于Apache Tika File MimeType Detect,另一个用于输出流。

private List<InputStream> copyInputStream(final InputStream pInputStream, final int numberOfCopies) throws UploadServiceException{
    final int bytesSize = 8192;
    List<InputStream> isList = null;        
    try(PushbackInputStream pushIS = new PushbackInputStream(pInputStream);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();){  
        byte[] buffer = new byte[bytesSize];
        for (int length = 0; ((length = pushIS.read(buffer)) > 0);) {
            baos.write(buffer, 0, length);
        }
        baos.flush();
        isList = new ArrayList();
        for(int i = 0; i < numberOfCopies ; i++){
            isList.add(new ByteArrayInputStream(baos.toByteArray()));
        }
    } catch (IOException ex) {
        throw new MyException(ErrorCodeEnum.IO_ERROR, ex);
    } catch (Exception ex) {            
        throw new MyException(ErrorCodeEnum.GENERIC_ERROR, ex);
    }
    return isList;
}

我看到一些性能问题

  1. 字节数组的大小是文件大小的两倍。我计划使用ByteArrayOutputStream(int size),但在上传过程中我没有文件大小。
  2. 我看到垃圾收集不经常发生,如何处理这种情况。
  3. 更新

    基于反馈

    • 删除了PushbackInputStream
    • 添加了最后一个字节[] byteArrayIS = baos.toByteArray();

      private List<InputStream> copyInputStream(final InputStream pInputStream, final int numberOfCopies) throws MyException{
          final int bytesSize = 8192;
          List<InputStream> isList = null;        
          try(ByteArrayOutputStream baos = new ByteArrayOutputStream();){  
              byte[] buffer = new byte[bytesSize];
              for (int length = 0; ((length = pInputStream.read(buffer)) > 0);) {
                  baos.write(buffer, 0, length);
              }
              baos.flush();
              isList = new ArrayList();
              final byte[] byteArrayIS = baos.toByteArray();
              for(int i = 0; i < numberOfCopies ; i++){
                  isList.add(new ByteArrayInputStream(byteArrayIS));
              }
          } catch (IOException ex) {
              throw new MyException(ErrorCodeEnum.IO_ERROR, ex);
          } catch (Exception ex) {
              if(ex instanceof MyException){
                  throw ex;
              }
              throw new MyException(ErrorCodeEnum.GENERIC_ERROR, ex);
          }
          return isList;
      }
      

2 个答案:

答案 0 :(得分:3)

  

字节数组的大小是文件大小的两倍。我打算使用ByteArrayOutputStream(int size),但在上传过程中我没有文件大小。

如果您必须使用ByteArrayOutputStream并且没有对大小进行良好估计,那么您无能为力。 ByteArrayOutputStream使用一种简单(且节省时间)的策略,在字节数组填满时将其加倍。

ByteArrayOutputStream的Apache Commons IO版本使用了一种可以减少复制的替代策略,但它仍然会过度分配内存......

  

我看到垃圾收集不经常发生,如何处理这种情况。

正确的方法是处理它。当JVM确定有必要时,让GC运行。到目前为止,这是Java中存储管理的最有效方式。

  • 使用System.gc()显式运行GC可能会对性能造成灾难性后果。
  • 运行GC(显式或让JVM执行此操作)不太可能将内存返回给操作系统。

事实上,GC不经常运行可能是的事情。

然后......查看你的代码......我可以看到一些东西意味着你将使用比你需要的更多的数据副本。

每次致电toByteArray()时,都会创建 ByteArrayOutputStream所捕获数据的新副本。对于你正在做的事情,这是不必要的。相反,您应该在创建单个toByteArray()后调用byte[]并将该单个byte[]包装在多个ByteArrayInputStream个实例中。您可以确定输入流不会修改byte[]中的字节。

在您的示例代码中使用PushbackInputStream并不能实现任何目标......在其他方面无法实现更好的目标。

答案 1 :(得分:1)

首先,为什么要使用PushbackInputStream?这完全无关紧要。如果inputStream尚未缓冲,您可能希望将InputStream包装到BufferedInputStream中。

其次,你是如何测量字节数组大小的? ByteArrayOutputStream自动管理内部分配。如果baos.toByteArray()给你双重数据,请先看看你实际从InputStream中读取了多少(提示:for循环中所有长度的总和)。

至于垃圾收集,它是自动的和不确定的,所以如果你不太了解它,那就别管它了。通常,较少的GC活动意味着有足够的内存可用和/或程序不会产生太多垃圾。这是件好事!但是,您应该确保一旦您不再需要它们就关闭所有流,否则您将收到内存泄漏。特别是,查找pInputStream关闭的位置,以及结果列表中所有InputStreams关闭的位置。