【问题标题】:ByteArrayOutputStream performanceByteArrayOutputStream 性能
【发布时间】:2017-08-08 09:32:57
【问题描述】:

我的要求是创建 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. 我看到垃圾收集并不经常发生,如何处理这种情况。

更新

根据反馈

  • 移除 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;
    }
    

【问题讨论】:

  • '字节数组的大小是文件大小的两倍'怎么测?其实什么字节数组?你为什么要使用ByteArrayOutputStream?将一个输入流复制到两个输出流并不难:编写TeeOutputStreamclass 很简单。
  • 我用VisualVM监控。
  • 监控什么?
  • 监控CPU,堆大小,byte[] size used
  • 我重复一遍。您通过这种方式测量了什么字节数组?

标签: java performance inputstream apache-tika bytearrayoutputstream


【解决方案1】:

字节数组的大小是文件大小的两倍。我计划使用 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 似乎并没有实现任何效果……其他方式无法更好地实现。

【讨论】:

  • 赞成,但我没有理由使用PushbackInputStreamBufferedInputStream。他正在使用 8k 缓冲区并且从不回溯。整个问题仍然模糊不清。
  • 是的……我意识到当我再次看他的阅读方式时……
【解决方案2】:

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

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

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

【讨论】:

  • 使用 InputStream.read(buffer) 抛出 NullPointerException 使用 PushbackInputStream.read(buffer) 抛出 IOException 来修复 NullPointerException。
  • @PrateekAgarwal NullPointerException 是一个微不足道的编程错误:输入流或缓冲区为空。如果这是这里的根本问题,您需要更新您的问题。
  • @PrateekAgarwal - 1) 缓冲区不是 null。看看你的代码! 2) 如果是null,那么NullPointerException正确 异常。将其隐藏为 IOException 只会使查找导致null 的错误变得更加困难。 3) 为什么不明确地测试null
猜你喜欢
  • 2011-06-02
  • 2011-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多