Избегайте ОШИБКИ JNI, генерируемой кодом Java в Android
Я разрабатываю тест для нескольких сотен регулярных выражений, которыми я должен управлять в Android. Я столкнулся с катастрофическим возвратом, который я не могу предотвратить (т. Е. Сопоставление входит в экспоненциальную сложность и кажется, что оно находится в бесконечном цикле, в то время как в действительности оно исследует очень большое количество возможных совпадений), поэтому мне нужно ограничить общее выполнение сопоставления с использованием тайм-аута.
Я уже нашел возможный подход здесь, но я также должен получить логическое возвращаемое значение из метода find(), поэтому Runnable - не лучший выбор. Даже небольшое изменение, предложенное среди других ответов в приведенной выше ссылке, во избежание использования потока не применимо, поскольку оно основано на расширении CharSequence, которое просто не работает, поскольку charAt не используется в matcher.find() (проверил это дважды, как с точкой останова во время отладки, так и с чтением источника Matcher). Изменить: Во второй раз я обнаружил, что @NullPointerException уже обнаружил, что charAt никогда не вызывается, но я не знаю, нашел ли он 3 года назад решение
Таким образом, лучший вариант, который я нашел до сих пор, кажется, использует FutureTask, который имеет возможность указать время ожидания, а также может вернуть значение. Я реализовал следующий код:
private boolean interruptMatch(final Matcher matcher){
boolean res = false;
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<Boolean> future =
new FutureTask(new Callable() {
public Boolean call() {
return matcher.find();
}
});
executor.execute(future);
try {
res = future.get(2000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Log.d("TESTER","Find interrupted after 2000 ms");
} catch (ExecutionException e) {
Log.d("TESTER","Find ExecException after 2000 ms");
} catch (TimeoutException e) {
Log.d("TESTER","Find timeout after 2000 ms");
}
future.cancel(true);
executor.shutdownNow();
return res;
}
Эта часть кода вызывается методом main почти "классическим" способом:
pattern = Pattern.compile(pattern, java.util.regex.Pattern.CASE_INSENSITIVE);
matcher = pattern.matcher(inputString);
if (interruptMatch(matcher)) { // before the need to manage catastrophic backtracking here there was a simple if (matcher.find()){
// Do something
}
Таким образом, все работало, по крайней мере, для первых нескольких сотен паттернов (что также ограничивало время ожидания катастрофической находки при возврате назад), пока я не получил следующую ошибку:
ОШИБКА JNI (ошибка приложения): слабое переполнение глобальной справочной таблицы (max=51200)
Он был сгенерирован с помощью вышеуказанного Java-кода (до того, как эта ошибка не появилась - отмена паттерна, который вызвал катастрофический откат назад, очевидно), но я не могу выяснить, как очистить глобальную справочную таблицу (я нашел несколько ответов о похожих проблемах, сгенерированных непосредственно кодом JNi, но не из Java), а не о том, как найти обходной путь или другой правильный подход.РЕДАКТИРОВАТЬ: я также попытался отладить, и я обнаружил, что проблема возникает, когда я вызываю метод get. Я пытался следовать коду FutureTask, но я не нашел ничего полезного (и мне очень скучно).
Не могли бы вы мне помочь? заранее спасибо
1 ответ
После других копаний я обнаружил, что в Android есть отслеживаемая проблема (кажется, что она связана с другими темами, но также отвечает и на мою), и из ответов я понимаю, что это просто проблема, которая появляется во время отладки. Я снова протестировал свое приложение-тестер и обнаружил, что это правда: без отладки вышеуказанная ошибка не происходит. Таким образом, серьезность проблемы намного ниже, и я могу жить с этим (для меня это закрытая проблема) -