【问题标题】:array length changes in openjdkopenjdk中的数组长度变化
【发布时间】:2015-05-11 12:36:19
【问题描述】:

我最近在我的应用程序中看到了我无法解释的奇怪行为。它们发生的频率很低,所以我无法重现它们。虽然这些问题发生的地方发生了变化,但共同的部分似乎是数组在创建后会改变大小(我知道,这是不可能的,因此:奇怪的行为)。

举几个例子来说明我的观点:

java.lang.StringIndexOutOfBoundsException: String index out of range: 86
    at java.lang.String.checkBounds(String.java:409)
    at java.lang.String.<init>(String.java:577)
    at com.acunia.fleet.diagnostics.providers.tacho.VDOKLineInputParser.getRealDriverID(Unknown Source)

导致这种情况的代码:

public String getRealDriverID(byte[] buffer) {
  if (buffer.length > 86 && isDriverCardInserted(buffer)) {
    return new String(buffer, 70, 16);
  }   
  return null;
}

所以我们首先检查缓冲区是否足够大(超过 86 个字节),然后再尝试从它们创建字符串。

第二个例子:

java.lang.ArrayIndexOutOfBoundsException: -1
    at java.lang.String.lastIndexOf(String.java:1889)
    at java.lang.String.lastIndexOf(String.java:1835)
    at java.lang.String.lastIndexOf(String.java:1817)
    at com.acunia.service.position.nmea.comm.CommPositionProvider.isValid(Unknown Source)

导致此异常的行是:

int csi = line.lastIndexOf("*");

我查看了 openjdk String.java 源代码,但找不到错误,除非数组在创建后突然开始改变大小。

我能找到的唯一可能相关的在线参考是一个 openjdk 错误:https://bugs.openjdk.java.net/browse/JDK-6817012。不过,这个错误被标记为“不是问题”,尽管在阅读本文时,我无法真正判断所提到的问题是否未被识别为错误,或者关闭错误的人是否不明白为什么会出现这个错误会引起问题。

如果有人遇到过类似的问题,我会非常感激听到它。就目前而言,这个问题太不稳定了,无法尝试使用其他版本的 openjdk 来修复它。

问题出现在:

hardware: custom arm platform
java version "1.6.0_31"
OpenJDK Runtime Environment (IcedTea6 1.13.3) (6b31-1.13.3-1~deb7u1)
OpenJDK Zero VM (build 23.25-b01, mixed mode)
Linux 3.2.0 #1 Fri Jun 20 10:25:16 CEST 2014 armv7l GNU/Linux

【问题讨论】:

  • 您正在使用 Java 6。Java 7 刚刚达到生命的尽头。你真的应该升级你的 Java。
  • 如果字符没有出现在字符串中,第二个例子很容易解释。在这种情况下,lastIndexOf 返回 -1,投诉是关于使用索引 -1。
  • 如果可能的话,我还建议您将 Java 升级到 8。最好是 Oracle 版本。 OpenJDK 让我感到头疼。
  • 该应用程序在遍布欧洲的嵌入式设备上运行(在移动的车辆中)。虽然更新这些是可能的,但代价高昂。此外,如果没有明确的迹象表明该问题在最近的 Java 版本中不再存在,我会感到不舒服。
  • lastIndexOf 如果字符没有出现应该返回 -1,但是它不应该抛出 ArrayIndexOutOfBoundsException

标签: java arrays linux openjdk


【解决方案1】:

这很可能是字节和系统字符集的问题。 The documentation 明确表示:

当给定字节在默认字符集中无效时,此构造函数的行为是未指定

您可以通过显式提供正确的字符集来规避这种情况:

new String(buffer, 70, 16, StandardCharsets.UTF_8)

【讨论】:

  • (对于 UTF-8 使用 StandardCharsets.UTF_8 代替,那么您不需要捕获 UnsupportedEncodingException,因为标准字符集始终与 JRE 一起分发。)
  • 不错。不知道那个班。更新。谢谢。
  • 我阅读了 String 类的源代码,它肯定与字符集无关(在尝试将字节转换为字符之前调用了 checkBounds)。
  • 给定无效字节,构造函数是未定义的,这很可能意味着“返回一个不知道自己边界的损坏的 String 对象”。长话短说:要么是系统默认字符集和输入字节之间不兼容的问题,要么是 JVM 或现在古老的(1.6)JRE 的字符串实现中存在一个模糊的错误。在前一种情况下,有一个合理的解释,在后一种情况下,几乎不可能以目前的形式回答问题。
【解决方案2】:

您正在使用byte[] 数组创建新的String,但未指定Charset。因此,我的猜测是系统默认字符集已更改,您会看到行为更改。我建议始终明确指定字符集。例如:

return new String(buffer, 70, 16, "UTF-8");

【讨论】:

  • 我们研究了这种可能性。该系统主要是只读的,因此无法更改默认字符集。此外,如果它被改变了,那么问题就会出现在很多地方,我们在整个运行过程中只看到过一次这样的异常。最后,java.lang.String 中的 checkBounds 方法似乎只根据字节数组的大小进行检查,它没有考虑 charset。
  • 字符集可能没有改变,但触发这种情况的系统输入可能并不经常发生?
  • 该特定线程每秒大约触发 4 次输入。其他线程的触发频率更高。
猜你喜欢
  • 2014-07-27
  • 2013-06-24
  • 2018-08-27
  • 1970-01-01
  • 1970-01-01
  • 2020-11-20
  • 1970-01-01
  • 2011-05-08
相关资源
最近更新 更多