来自ResponseBody的RestTemplate InputStream使用的内存超出预期

时间:2017-09-18 22:40:29

标签: java spring resttemplate

我需要从服务器下载潜在的大文件。为了避免潜在的OutOfMemoryExceptions,我想使用InputStreams来防止一次将整个文件放入内存。

我决定运行一个(粗略的)基准测试,以确定从以下SO回答中给出的方法是否会减少内存占用:

https://stackoverflow.com/a/38664475/2434579

使用这个例子,我编写了这段代码,看看在任何给定时间内使用了多少内存:

  public void memoryTest(){
    // 20MB file: "https://www.hq.nasa.gov/alsj/a17/A17_FlightPlan.pdf"
    // Small file: "https://upload.wikimedia.org/wikipedia/en/7/7e/Patrick_Star.png"
    RequestCallback requestCallback = request -> request.getHeaders()
        .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
    ResponseExtractor<Void> responseExtractor = response -> {
      try {
        InputStream is = response.getBody();
        OutputStream outstream = new OutputStream(){
          @Override
          public void write(int b) throws IOException {
          }
        };
        int size = 32528;  // This value is the buffer size I get when I get a buffer size from an oracle.sql.BLOB instance
        byte[] buffer = new byte[size];
        int length = -1;
        System.out.println("Before writing - MB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024) + " / " + Runtime.getRuntime().totalMemory() / (1024 * 1024));
        while ((length = is.read(buffer)) != -1) {
          outstream.write(buffer, 0, length);
        }
        System.out.println("After writing - MB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024) + " / " + Runtime.getRuntime().totalMemory() / (1024 * 1024));
        is.close();
        outstream.close();
      } catch(Exception e){
        e.printStackTrace();
      }
      return null;
    };
    restTemplate.execute(URI.create("https://www.hq.nasa.gov/alsj/a17/A17_FlightPlan.pdf"), HttpMethod.GET, requestCallback, responseExtractor);
  }

当我下载20 MB文件时,这就是我的println语句所说的:

Before writing - MB: 134.33865356445312 / 627
After writing - MB: 214.2599334716797 / 627

当我下载小文件(小于1 MB)时,这就是我的println语句所说的:

Before writing - MB: 126.80110931396484 / 627
After writing - MB: 128.01902770996094 / 627

我看到当我下载20MB文件时,内存使用量增加了大约80 MB。我下载较小的文件时使用率很低(2MB)。我知道流是最好的做法,但从这些数据来看,我并不完全相信这个解决方案最能解决我的问题。

我的问题如下:

  1. 当我打开一个下载20 MB文件的InputStream时,为什么使用量会增加80 MB?
  2. 如果100个用户点击此端点并且都在下载大文件(不一定是同一个文件),可能会发生OutOfMemoryException吗?换句话说,这个代码是否明显优于将整个文件放入内存?

0 个答案:

没有答案