简体   繁体   English

Java Inflater 有时会无限循环

[英]Java Inflater will loop infinitely sometimes

In my application, I'm trying to compress/decompress byte array using java's Inflater/Deflater class.在我的应用程序中,我尝试使用 java 的 Inflater/Deflater 类压缩/解压缩字节数组。 Here's part of the code I used at first:这是我最初使用的代码的一部分:

   ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);  
   byte[] buffer = new byte[1024];  
   while (!inflater.finished()) {  
       int count = inflater.inflate(buffer);  
       outputStream.write(buffer, 0, count);  
   }  

Then after I deployed the code it'll randomly (very rare) cause the whole application hang, and when I took a thread dump, I can identify that one thread hanging然后在我部署代码后,它会随机(非常罕见)导致整个应用程序挂起,当我进行线程转储时,我可以识别出一个线程挂起

    at java.util.zip.Inflater.inflateBytes(Native Method)
    at java.util.zip.Inflater.inflate(Inflater.java:259)
      - locked java.util.zip.ZStreamRef@fc71443
    at java.util.zip.Inflater.inflate(Inflater.java:280)

It doesn't happen very often.它不经常发生。 Then I googled everywhere and found out it could be some empty byte data passed in the inflater and finished() will never return true.然后我到处搜索,发现它可能是在充气器中传递的一些空字节数据,而finished()永远不会返回true。

So I used a workaround, instead of using所以我使用了一种解决方法,而不是使用

while (!inflater.finished()) 

to determine if it's finished, I used为了确定它是否完成,我使用了

while (inflater.getRemaining() > 0)

But it happened again.但它又发生了。 Now it makes me wonder what's the real reason that causes the issue.现在它让我想知道导致问题的真正原因是什么。 There shouldn't be any empty array passed in the inflater, even if it did, how come getRemaining() method did not break the while loop? inflater 中不应该传递任何空数组,即使传递了,为什么 getRemaining() 方法没有中断 while 循环? Can anybody help pls?有人可以帮忙吗? It's really bugging me.真是烦死我了。

Confused by the same problem, I find this page.对同样的问题感到困惑,我找到了这个页面。

This is my workaround for this, it may helps:这是我的解决方法,它可能会有所帮助:

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (!inflater.finished()) {
    int i = inflater.inflate(buffer);
    if (i == 0) {
        break;
    }
    byteArrayOutputStream.write(buffer, 0, i);
}

The javadoc of inflate :对Javadoc 充气

Uncompresses bytes into specified buffer.将字节解压缩到指定的缓冲区中。 Returns actual number of bytes uncompressed.返回未压缩的实际字节数。 A return value of 0 indicates that needsInput() or needsDictionary() should be called in order to determine if more input data or a preset dictionary is required.返回值 0 表示应调用 NeedsInput() 或 NeedsDictionary() 以确定是否需要更多输入数据或预设字典。 In the latter case, getAdler() can be used to get the Adler-32 value of the dictionary required.在后一种情况下, getAdler() 可用于获取所需字典的 Adler-32 值。

So @Wildo Luo was certainly right to check for 0 being returned.所以@Wildo Luo 检查是否返回 0 肯定是正确的。

byte[] buffer = new byte[1024];  
while (!inflater.finished()) {  
    int count = inflater.inflate(buffer);  
    if (count != 0 ) {
       outputStream.write(buffer, 0, count);  
    } else {
       if (inflater.needsInput()) { // Not everything read
           inflater.setInput(...);
       } else if (inflater.needsDictionary()) { // Dictionary to be loaded
           inflater.setDictionary(...);
       }
    }
}
inflater.end();

I can only imagine that elsewhere the code is not entirely right, maybe on the compression size.我只能想象其他地方的代码并不完全正确,也许是压缩大小。 Better first check the general code.最好先检查通用代码。 There is the Inflater(boolean nowrap) requiring an extra byte, the end() call. Inflater(boolean nowrap)需要一个额外的字节,即end()调用。 Exception handling (try-finally).异常处理(try-finally)。 Etcetera.等等。

For unkown data, unknown occurrences: using a try-catch, find compressed data to check whether it is a data based error, and for testing any solution.对于未知数据,未知事件:使用 try-catch,查找压缩数据以检查它是否是基于数据的错误,并测试任何解决方案。

Having the same problem...遇到同样的问题...

What I'm sure about:我确定的是:

  1. I'm having an infinite loop, assured with logs printed.我有一个无限循环,保证打印日志。
  2. inflater.inflate returns 0, and the output buffer size is 0. inflater.inflate返回 0,输出缓冲区大小为 0。

My loop is like this (Hive ORC code):我的循环是这样的(Hive ORC 代码):

while (!(inflater.finished() || inflater.needsDictionary() ||
             inflater.needsInput())) {
      try {
        int count = inflater.inflate(out.array(),
                                     out.arrayOffset() + out.position(),
                                     out.remaining());
        out.position(count + out.position());
      } catch (DataFormatException dfe) {
        throw new IOException("Bad compression data", dfe);
      }
    }

After the out buffer is consumed and its remaining size is 0, the loop will infinitely run.消耗完输出缓冲区并且其剩余大小为 0 后,循环将无限运行。

But I'm not sure about whether it's orc or zlib caused this.但我不确定是orc 还是zlib 导致了这个。 On orc side, it fills original data with the same compression buffer size then do the compression, so theoretically it's not possible I get an compressed chunk larger than the buffer size.在兽人方面,它用相同的压缩缓冲区大小填充原始数据然后进行压缩,所以理论上我不可能得到大于缓冲区大小的压缩块。 Possibilities may be zlib or hardware.可能性可能是 zlib 或硬件。

That being said, break the loop when count == 0 is dangerous, since there may be still uncompressed data in the inflator.话虽如此,当count == 0很危险时中断循环,因为充气机中可能仍有未压缩的数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM