【问题标题】:final attributes that change最终改变的属性
【发布时间】:2012-01-30 20:13:26
【问题描述】:

我们遇到了一个目前无法解决的错误,没有明显的原因或解决方案。

该错误发生在对包含从文件加载的数据的数组的多次访问中。 ArrayIndexOutOfBounds 出现在与此数组一起使用的代码的各个部分中。

变量似乎随机改变值。为了防止这种情况,我们尝试将修饰符“final”放在所有类属性中。但他们仍然随机改变值。

下面,方法“execute()”及其内循环“while”是发生错误的应用点。

public Set<Long> execute(InputStream datIn, int qtd) throws PersistenceException {

        if (this.criterion == null) {
            throw new PersistenceException("Não foi especificado filtro para a busca");
        }

        this.criterion.configure(info);

        try {
            Set<Long> retorno = new HashSet<Long>(qtd);
            byte[] b = new byte[Serializator.BUFFER_SIZE];
            int bl = datIn.read(b, 0, Serializator.BUFFER_SIZE);
            int br = bl, bp = 0, nbp = 0, blockSize, i, p = 0;

            while (p++ < qtd) {
                System.out.println("P = "+p);
                bp = nbp;
                int aux = ByteArrayUtils.toUnsignedShort(b, bp);
                System.out.println("aux = "+aux);
                blockSize = aux + indexBlockSize;
                System.out.println("indexBlockSize = "+indexBlockSize);
                System.out.println("blockSize = "+blockSize);
                if (br < blockSize + 2) {
                    for (i = 0; i < br; i++) {
                        b[i] = b[i + bp];
                    }
                    bl = datIn.read(b, br, Serializator.BUFFER_SIZE - br) + br;
                    bp = 0;
                    br = bl;
                }

                nbp = bp + blockSize;
                br -= blockSize;

                System.out.println("b.length = "+b.length);
                System.out.println("bp = "+bp);
                System.out.println("headerSize = "+headerSize);
                if (this.criterion.doCompare(b, bp, headerSize)) {
                    retorno.add(getSearchedValue(b, bp));
                    if (retorno.size() == this.maxResults) {
                        break;
                    }
                }
            }

            return retorno;
        } catch (IOException e) {
            throw new PersistenceException(e);
        }
}

下面是出现在“execute()”中的方法的代码:

获取搜索值:

public long getSearchedValue(byte[] b, int bp) {
    if (this.fieldReturn == -1) {
        return ByteArrayUtils.toLong(b, bp + 2);
    } else {
        if (b[this.bitSetPosition + bp] >= 0) {
            int off = ByteArrayUtils.toShort(b, bp + rIndex) + headerSize + bp;
            return ByteArrayUtils.readOptimizedLong(b, off);
        } else {
            return 0;
        }
    }
}

ByteArraysUtils.toShort():

public static short toShort(byte[] byteArray, int off) {
        System.out.println("ByteArrayUtils: toShort: byteArray.length="+byteArray.length
                +" off = "+off);
        return (short) ((byteArray[off + 1] & 0xFF) | ((byteArray[off] & 0xFF) << 8));
}

ByteArraysUtils.toUnsignedShort():

public static int toUnsignedShort(byte[] byteArray, int off) {
        System.out.println("ByteArrayUtils: toUnsignedShort: byteArray.length="+byteArray.length
                +" off = "+off);
        return ((int) 0) | ((byteArray[off + 1] & 0xFF) | ((byteArray[off] & 0xFF) << 8));
}

Criterion.doCompare():

public boolean doCompare(byte[] data, int offset, int headerSize) throws PersistenceException {
    System.out.println("doCompare: data.length = "+data.length+" offset = "+offset
            +" this.position = "+this.position);
    if (data[offset + this.position] >= 0) {
        short off = ByteArrayUtils.toShort(data, offset + this.position);
        return this.def.verify(data, off + offset + headerSize);
    } else {
        return this.def.verifyNull();
    }
}

以下是执行过程中产生的一些错误日志:

  • 错误 1: 引起:java.lang.ArrayIndexOutOfBoundsException 在 com.wealthsystems.util.api.array.ByteArrayUtils.toShort(ByteArrayUtils.java:123) 在 com.wealthsystems.persistence.impl.search.criterion.Criterion.doCompare(Criterion.java:64) 在 com.wealthsystems.persistence.impl.search.DatSearch.execute(DatSearch.java:133)

  • 错误 2: 6841 LoginActivity Falha de login java.lang.ArrayIndexOutOfBoundsException java.lang.ArrayIndexOutOfBoundsException 在 com.wealthsystems.persistence.impl.search.criterion.Criterion.doCompare(Criterion.java:61)

  • 错误 3: 1002575 LoginActivity Falha de login java.lang.ArrayIndexOutOfBoundsException java.lang.ArrayIndexOutOfBoundsException 在 com.wealthsystems.util.api.array.ByteArrayUtils.toUnsignedShort(ByteArrayUtils.java:129)

正如我们所见,错误发生在许多不同的位置,但变量的值始终相同,即错误似乎是随机的。这通过变量的打印得到了验证(因为当我们打开调试时不会发生错误),并且通过打印我们看到变量的奇怪变化。以下是生成的一些打印示例:

打印 1:

P = 746
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 22400
aux = 100
indexBlockSize = 58
blockSize = 158
b.length = 32767
bp = 22400
headerSize = 65
doCompare: data.length = 32767 offset = 22400 this.position = 10
ByteArrayUtils: toShort: byteArray.length=32767 off = 22410
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 22465

P = 747
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 22558
aux = 99
indexBlockSize = 58
blockSize = 157
b.length = 32767
bp = 22558
headerSize = 65
doCompare: data.length = 32767 offset = 22558 this.position = 10
java.lang.ArrayIndexOutOfBoundsException
     at com.wealthsystems.persistence.impl.search.criterion.Criterion.doCompare(Criterion.java:62)
     at com.wealthsystems.persistence.impl.search.DatSearch.execute(DatSearch.java:121)
     at com.wealthsystems.persistence.impl.retrieve.DatRetrieve.search(DatRetrieve.java:62)

注意,在“doCompare()”中会访问索引“[offset + this.position]”,但是在索引访问时错误被指责为22568。索引小于向量的大小(“data.长度”)。

打印 2:

P = 578
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 28651
aux = 114
indexBlockSize = 58
blockSize = 172
b.length = 32767
bp = 28651
headerSize = 65
doCompare: data.length = 32767 offset = 28651 this.position = 10
ByteArrayUtils: toShort: byteArray.length=32767 off = 28661
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 28716

P = 579
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 28823
aux = 114
indexBlockSize = 58
blockSize = 22618
b.length = 32767
bp = 0
headerSize = 65
doCompare: data.length = 32767 offset = 0 this.position = 10
ByteArrayUtils: toShort: byteArray.length=32767 off = 10
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 65

P = 580
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 22618
aux = 65280
indexBlockSize = 58
blockSize = 87784
b.length = 32767
bp = 0
headerSize = 65
doCompare: data.length = 32767 offset = 0 this.position = 10
P = 581
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 87784
28493 LoginActivity Falha de login java.lang.ArrayIndexOutOfBoundsException
java.lang.ArrayIndexOutOfBoundsException
at com.wealthsystems.util.api.array.ByteArrayUtils.toUnsignedShort(ByteArrayUtils.java:131)
at com.wealthsystems.persistence.impl.search.DatSearch.execute(DatSearch.java:98)
at com.wealthsystems.persistence.impl.retrieve.DatRetrieve.search(DatRetrieve.java:62)

注意执行 p = 579。 aux值是114,indexBlockSize的值是58,blockSize的值应该是172,但是出现blockSize=22618的值。

打印 3:

P = 1
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 0
aux = 144
indexBlockSize = 58
blockSize = 202
b.length = 32767
bp = 0
headerSize = 65
doCompare: data.length = 32767 offset = 0 this.position = 10
ByteArrayUtils: toShort: byteArray.length=32767 off = 10
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 65

P = 2
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 202
aux = 96
indexBlockSize = 58
blockSize = 154
b.length = 32767
bp = 202
headerSize = 65
doCompare: data.length = 32767 offset = 202 this.position = 10
ByteArrayUtils: toShort: byteArray.length=32767 off = 212
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 267

P = 3
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 356
aux = 115
indexBlockSize = -7696
blockSize = -7581
b.length = 32767
bp = 356
headerSize = 65
doCompare: data.length = 32767 offset = 356 this.position = 10
ByteArrayUtils: toShort: byteArray.length=32767 off = 366
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = 421

P = 4
ByteArrayUtils: toUnsignedShort: byteArray.length=32767 off = -7225 61680
LoginActivity Falha de login java.lang.ArrayIndexOutOfBoundsException java.lang.ArrayIndexOutOfBoundsException
at com.wealthsystems.util.api.array.ByteArrayUtils.toUnsignedShort(ByteArrayUtils.java:131)
at com.wealthsystems.persistence.impl.search.DatSearch.execute(DatSearch.java:98)
at com.wealthsystems.persistence.impl.retrieve.DatRetrieve.search(DatRetrieve.java:62)

这是最严重的错误,因为变量“indexBlockSize”是私有的并且在构造函数中初始化并且不再修改,但是在运行过程中它改变了indexBlockSize = -7696的值(执行P = 3) .

发生错误的设备:
- iBAK-775(固件:2.1-update1-1.0.0)(内核:2.6.25)(频率:错误非常常见);
- LG 擎天柱一号 P500(Android 版本:2.2.2)(内核:2.6.32.9)(频率:错误不常见);
- 三星 Galaxy Tab GT-P1000 (Android: 2.2) (频率: 常见错误);
- 三星 I5800 Galaxy 3 (Android 2.2)(频率:常见错误)。

从未发生错误的设备:
- 以上所有设备,在 Eclipse 中以调试模式运行时;
- 任何版本的 Android 上的模拟器;
- 摩托罗拉 Spice XT300(固件:2.1-update1)(内核:2.6.29);
- 三星 Galaxy 5 (GT-I5500B)(固件:2.1-update1)(内核:2.6.29);
- 摩托罗拉 Xoom (Android 3.0);
- 三星 Galaxy Tab 8.9 (Android 3.0)。

很遗憾,我们无法将错误隔离到我们的应用程序的外部代码中,因此您能够重现该错误。所有将代码从我们的应用程序中隔离出来的尝试都没有产生错误。但希望你能给我们一些关于我们正在发生的事情的信息!

我们在互联网上对这个错误的研究只显示了这个页面:http://www.androiddiscuss.com/1-android-discuss/96129.html。看起来有人遇到了与我们类似的问题,但讨论中没有解决方案。


属性声明和类构造函数:

private byte fieldReturn;
private EntityInfo info;
private final short rIndex, indexBlockSize;
private ICriterion criterion = null;
private int headerSize;
private int maxResults;
private int bitSetPosition;

public DatSearch(byte fieldReturn, EntityInfo info) {
    this.fieldReturn = fieldReturn;
    this.bitSetPosition = (this.fieldReturn == -1) ? -1 : 10 + (fieldReturn * 2);
    this.info = info;
    this.rIndex = (fieldReturn >= 0) ? (short) (10 + (fieldReturn << 1)) : 0;
    short qtdIndices = (short) info.getMapper().getIndexes().length;
    this.indexBlockSize = (short) (10 + (qtdIndices << 1));
    this.headerSize = indexBlockSize + info.getBitSetLength();
}

我尝试将这个类的所有变量放在外面的另一个类中,并用gets和sets读取它,但是这种解决方法不起作用。

【问题讨论】:

  • 1.有多少线程正在这样做? 2. 你到底是如何定义 indexBlockSize 变量的?而final关键字仅表示您不能在其范围内更改变量的值,并且该变量超出范围并且可以修改。我还有一个想法,请展示你的 indexBlockSize 变量的所有用法以及它是如何定义的。谢谢。
  • 您还可以阅读有关 java 中的可变和不可变对象/类型以及垃圾回收的信息
  • 1.只有一根线。 2.我添加了属性声明和类构造函数。 indexBlockSize 变量仅在该方法中使用,从未写入。
  • 你确定 DatSearch 只被调用一次吗?将您的变量设为private static final short indexBlockSize;
  • 调用了很多次,但都是同一个线程,我们通过调试打印当前线程名确认了这一点。此外,它总是创建一个新的 DatSearch 实例,并且该方法只在该实例上调用一次,这应该可以防止线程问题。

标签: java android arrays concurrency scope


【解决方案1】:

我假设您多次调用您的执行代码。在这种情况下,不同的线程可能正在访问您的数组。当一个正在更改值时,另一个线程可能正在读取它们......

看看java synchronized 关键字。该关键字用于防止不同线程访问相同的值。 more about synchronization

因此,当从您的数组读取/写入数据时,请使用同步的方法将这些调用包围起来,例如:

byte[] b;
synchronized(b)
{
    byte[] b = new byte[Serializator.BUFFER_SIZE];
}

synchronized(b)
{
    b[i] = b[i + bp];
}

您也可以为完整的功能执行此操作:

public synchronized long getSearchedValue(byte[] b, int bp) {
    ... your code
}

请务必这样做,无论您在哪里更改崩溃的数组。

【讨论】:

  • 调用方法的总是同一个线程。我们同步了对 b 和 execute() 方法的所有访问。但它没有用。
  • 在那种情况下,我不知道。对不起!
【解决方案2】:

此问题仅出现在旧设备和旧 Android 版本中。 2.3以上版本不会发生。

【讨论】:

    猜你喜欢
    • 2020-04-28
    • 2018-12-31
    • 2010-11-27
    • 2021-02-17
    • 2021-01-05
    • 1970-01-01
    • 2011-10-31
    • 1970-01-01
    相关资源
    最近更新 更多