2015-02-16 6 views
6

У меня есть две различные реализации функции (например, размер дерева), которая является рекурсивной и одна использует явный стек.Безопасно ли поймать StackOverflowError в Java?

Рекурсивный очень быстрый (вероятно, потому, что ему не нужно ничего выделять в куче), но может вызвать переполнение стека на некоторых «редких» входах (в примере дерева это было бы на любом неуравновешенном дереве) , Явная версия медленнее, но вряд ли вызовет переполнение стека.

Насколько безопасно использовать рекурсивную реализацию по умолчанию и восстановить из исключения StackOverflowError, выполнив явный вариант?

Это считается плохой практикой?

Вот небольшой пример кода:

interface Node { 
    List<? extends Node> getSons(); 
} 

static int sizeRec (Node root) { 
    int result = 1; 
    for (Node son : root.getSons()) { 
    result += sizeRec(son); 
    } 
    return result; 
} 

static int sizeStack (Node root) { 
    Stack<Node> stack = new Stack<Node>(); 
    stack.add(root); 
    int size = 0; 
    while (! stack.isEmpty()) { 
    Node x = stack.pop(); 
    size ++; 
    for (Node son : x.getSons()) { 
     stack.push(son); 
    } 
    } 
    return size; 
} 

static int size (Node root) { 
    try { 
    return sizeRec(root); 
    } catch (StackOverflowError e) { 
    return sizeStack(root); 
    } 
} 
+0

AFAIK нет никаких проблем с ловли StackOverflowError - это просто очень редко, что нужно сделать, и * большую часть времени * это указывает на ошибку, а не структуры данных слишком сложным для обработки рекурсивно , – immibis

+0

Как вы оцениваете быстрее и медленнее в этом случае? Вы сравнили это? Если да, то как? –

+3

@immibis: Я не уверен. Если вы поймаете исключение, то что? Вероятно, ваш стек не находится в состоянии ожидания, и как бы вы восстановились после такой ошибки и убедитесь, что ваша программа стабильна? –

ответ

3

Ну, это дело вкуса. Но я не верю, что ты должен это делать. Во-первых, ваша логика искажает смысл обработки исключений (исключения для «исключений» не для логики), другие программисты будут иметь проблемы с интерпретацией вашего кода.

Кроме того, вы не должны ловить «Erros», которые указывают на проблемы во время работы в среде. Вы должны спросить себя, стоит ли забыть о хороших практиках. Возможно, вы могли бы попытаться настроить конфигурацию времени выполнения, чтобы она соответствовала приложению или добавила дополнительную логику проверки ... ваш вызов там ... но, поскольку безопасность рассматривается, вы не можете сказать, что все в порядке, поскольку мы не знаем, как состояние стека в настоящее время, и различные реализации JRE могут различаться там.

Наконец, вопрос на дне: это плохая практика, и это не безопасно.

Цитата из https://softwareengineering.stackexchange.com/questions/209099/is-it-ever-okay-to-catch-stackoverflowerror-in-java:

surely there are situations where a stack overflow might leave an application inconsistent just like a memory exhaustion. Just imagine that some object is constructed and then initialized with the help of nested internal method calls - if one of them throws, the object may very well be in a state not supposed to be possible, just as if an allocation had failed. But that doesn't mean that your solution couldn't still be the best one

Это повод назвать ошибки, а не исключение ...

От Docs:

public abstract class VirtualMachineError extends Error: Thrown to indicate that the Java Virtual Machine is broken or has run out of resources necessary for it to continue operating

public class Error extends Throwable: An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a "normal" condition, is also a subclass of Error because most applications should not try to catch it. A method is not required to declare in its throws clause any subclasses of Error that might be thrown during the execution of the method but not caught, since these errors are abnormal conditions that should never occur. That is, Error and its subclasses are regarded as unchecked exceptions for the purposes of compile-time checking of exceptions.

6

Я хотел бы предложить, что в вашем sizeRecursive вы поддерживаете счетчик глубины стека, и если вы превысите указанный уровень, переключитесь на метод sizeStackUsingHeap. Не полагайтесь на исключение StackOverflow - это плохая практика. Вы не должны использовать исключения для определения своего алгоритма.

interface Node { 

    List<? extends Node> getSons(); 
} 

// Switch to a heap stack if the stack ever hits this level. 
private static final int STACKLIMIT = 1000; 

private static int sizeRecursive(Node root) { 
    // Start the stack depth at 0. 
    return sizeRecursive(root, 0); 
} 

// Recursive implementation. 
private static int sizeRecursive(Node root, int depth) { 
    int result = 1; 
    for (Node son : root.getSons()) { 
     if (depth < STACKLIMIT) { 
      result += sizeRecursive(son, depth + 1); 
     } else { 
      // Too deep - switch to heap. 
      result += sizeUsingHeap(son); 
     } 
    } 
    return result; 
} 

// Use this when the stack gets realy deep. It maintains the stack in the heap. 
private static int sizeUsingHeap(Node root) { 
    Stack<Node> stack = new Stack<>(); 
    stack.add(root); 
    int size = 0; 
    while (!stack.isEmpty()) { 
     // I am assuming this algorithm works. 
     Node x = stack.pop(); 
     size++; 
     for (Node son : x.getSons()) { 
      stack.push(son); 
     } 
    } 
    return size; 
} 

// Always use sizeRecursive to begin with. 
public static int size(Node root) { 
    return sizeRecursive(root); 
} 
+0

Почему это плохая практика? – immibis

+0

@immibis - По многим причинам - 1. вы используете инструмент для функции, для которой он не предназначен - 2. Очень дорого создать исключение, поскольку должна быть построена полная трассировка стека. Если доступен другой алгоритм (как я уже опубликовал), то это должно использоваться для повышения эффективности - см. Также [См. [Почему исключения не используются как регулярный поток управления?] (Http://stackoverflow.com/questions/729379/why- не потребительное исключение-как-регулярный поток из-под контроль). – OldCurmudgeon

+0

, если правило «не использовать исключения для управления потоком, полная остановка», то я думаю, что улавливание NumberFormatException также плохое. – immibis

Смежные вопросы