Как реализовать закрытия без gc?

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

blockquote>

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.
blockquote>

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа перестает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).

12
задан Joel Coehoorn 7 December 2011 в 19:23
поделиться

11 ответов

Это было бы лучшим вопросом, если можно объяснить, чего Вы стараетесь избегать, не используя GC. Поскольку я уверен, что Вы знаете, большинство языков, которые обеспечивают лексические закрытия, выделяет их на "куче" и позволяет им сохранять ссылки на привязки переменных в записи активации, которая создала их.

единственная альтернатива тому подходу, о котором я знаю, что gcc использование для вложенных функций: создайте батут для функции и выделите ее на стеке. Но поскольку в gcc руководстве говорится:

, При попытке назвать вложенную функцию через ее адрес после того, как содержание функции вышло, весь ад вырвется на свободу. При попытке назвать его после того, как содержание уровня объема вышло, и если это относится к некоторым переменным, которые больше не находятся в объеме, можно быть удачливыми, но не мудро рискнуть. Если, однако, вложенная функция не относится ни к чему, что вышло из объема, необходимо быть в безопасности.

Короткая версия, у Вас есть три основных варианта:

  • выделяют закрытия на стеке и не позволяют их использование после их содержания функциональных выходов.
  • выделяют закрытия на "куче" и используют сборку "мусора" некоторого вида.
  • проводят, исходное исследование, возможно, начинающее с региона, наполняет тот ML, Циклон, и т.д. имеет.
13
ответ дан 2 December 2019 в 03:12
поделиться

Этот поток мог бы помочь, хотя некоторые ответы здесь уже отражают ответы там.

Один плакат делает правильное замечание:

Кажется желанием сборки "мусора" для закрытий "в отсутствие истинной сборки "мусора"". Обратите внимание, что закрытия могут использоваться для реализации ячеек недостатков. Так Ваш вопрос, кажется, о сборке "мусора" "в отсутствие истинной сборки "мусора"" - существует богатая связанная литература. Ограничение проблемы к закрытиям действительно не изменяет его.

Таким образом, ответ: нет, нет никакого изящного способа иметь закрытия и никакой реальный GC. Лучшим, которое можно сделать, является некоторое взламывание для ограничения закрытий конкретным типом закрытия. Все это бесполезно, если у Вас есть надлежащий GC.

Так, мой вопрос отражает некоторые из других здесь - почему Вы не хотите реализовывать GC? Простой mark+sweep или stop+copy проводят приблизительно 2-300 строк (Схемы) кода и не являются действительно этим плохо с точки зрения работы по программированию. С точки зрения создания Ваших программ медленнее:

  1. Можно реализовать более сложный GC, который имеет лучшую производительность.
  2. Просто думайте обо всех программах утечек памяти в своем языке, не пострадает от.
  3. Кодирование с доступным GC является благословением. (Думайте C#, Java, Python, Perl, и т.д.... по сравнению с C++ или C).
9
ответ дан 2 December 2019 в 03:12
поделиться

Я понимаю, что очень опоздал, но случайно наткнулся на этот вопрос.

Я считаю, что полная поддержка замыканий действительно требует GC , но в некоторых особых случаях выделение стека безопасно. Определение этих особых случаев требует некоторого анализа побега. Я предлагаю вам взглянуть на статьи на языке BitC , такие как Реализация замыкания в BitC . (Хотя я сомневаюсь, что документы отражают текущие планы.) У разработчиков BitC была та же проблема, что и у вас. Они решили реализовать специальный режим не для сбора для компилятора, который запрещает все замыкания, которые могут сбежать. Если включено, это значительно ограничит язык. Однако эта функция еще не реализована.

Я бы посоветовал вам использовать коллектор - это самый элегантный способ. Также следует учитывать, что хорошо построенный сборщик мусора выделяет память быстрее, чем malloc. Пользователи BitC действительно ценят производительность, и они все еще думают, что GC подходит даже для большинства частей их операционной системы, Coyotos. Недостатки можно перенести простыми средствами:

  • создать только минимальное количество мусора
  • , позволяя программисту управлять сборщиком
  • , оптимизировать использование стека / кучи с помощью escape-анализа
  • использовать инкрементный или параллельный сборщик
  • ] если возможно, разделите кучу, как это делает Эрланг

Многие боятся сборщиков мусора из-за своего опыта работы с Java. У Java фантастический сборщик, но приложения, написанные на Java, имеют проблемы с производительностью из-за огромного количества создаваемого мусора. К тому же,

9
ответ дан 2 December 2019 в 03:12
поделиться

C++ 0x спецификация определяет лямбды без сборки "мусора". Короче говоря, спецификация позволяет недетерминированное поведение в случаях, где закрытие лямбды содержит ссылки, которые больше не действительны. Например (псевдосинтаксис):

(int)=>int create_lambda(int a)
{
    return { (int x) => x + a }
}

create_lambda(5)(4)    // undefined result

лямбда в этом примере относится к переменной (a), который выделяется на стеке. Однако тот стековый фрейм был вытолкан и не обязательно доступен, как только функция возвращается. В этом случае это, вероятно, работало бы и возвратилось бы 9 в результате (принимающий нормальную семантику компилятора), но нет никакого способа гарантировать это.

, Если Вы избегаете сборки "мусора", тогда я предполагаю, что Вы также позволяете явную "кучу" по сравнению с выделением стека и (вероятно) указателями. Если это так, тогда можно сделать как C++ и просто предположить, что разработчики, использующие язык, будут достаточно умны, чтобы определить проблемные случаи с лямбдами и скопировать в "кучу" явно (точно так же, как Вы были бы при возврате значения, синтезируемого в функции).

4
ответ дан 2 December 2019 в 03:12
поделиться

подсчет ссылок Использования и собирает "мусор" циклы (мне действительно не нравится это)

, возможно разработать Ваш язык, таким образом, нет никаких циклов: если можно только сделать новые объекты и не видоизменить старые, и если создание объекта не может сделать цикл, то циклы никогда не появляются. Erlang работает по существу этот путь, хотя на практике он действительно использует GC.

4
ответ дан 2 December 2019 в 03:12
поделиться

Если у Вас есть оборудование для точного GC копирования, Вы могли бы выделить на стеке первоначально и скопировать в "кучу" и обновить указатели, если Вы обнаруживаете в выходе, что указатель на этот стековый фрейм вышел. Тем путем Вы только платите при фактическом получении закрытия, которое включает этот стековый фрейм. Помогает ли это, или вред зависит от того, как часто Вы используете закрытия и сколько они получают.

Вы могли бы также изучить подход 0x C++ ( N1968), хотя, поскольку можно было бы ожидать от C++, он состоит из рассчитывания на программиста для определения то, что копируется и на что ссылаются, и если Вы получаете его неправильно, Вы просто получаете недопустимые доступы.

3
ответ дан 2 December 2019 в 03:12
поделиться

Или просто не делайте GC вообще. Могут быть ситуации, где лучше просто забыть утечку памяти и позволить процессу вымыться после него, когда это сделано.

В зависимости от Ваших приступов растерянности о GC, Вы могли бы бояться периодических разверток GC. В этом случае Вы могли сделать выборочный GC, когда объект падает из объема или изменений указателя. Я не уверен, насколько дорогой это было бы все же.

@Allen

, Что такое хороший закрытие, если Вы не можете использовать их, когда содержание функции выходит? Из того, что я понимаю, что это - смысл закрытий.

2
ответ дан 2 December 2019 в 03:12
поделиться

Вы могли работать учитывая, что все закрытия назовут в конечном счете и точно одно время. Теперь, когда закрытие называют, можно сделать очистку при возврате закрытия.

Как Вы планируете контакт с возвратом объектов? Они должны быть очищены в какой-то момент, который является той же самой проблемой с закрытиями.

2
ответ дан 2 December 2019 в 03:12
поделиться

Создать несколько стеков?

0
ответ дан 2 December 2019 в 03:12
поделиться

Я считал, что последние версии ML используют GC только экономно

0
ответ дан 2 December 2019 в 03:12
поделиться

Лучше поздно, чем никогда?

Это может быть вам интересно: Differential Execution.

Это малоизвестный метод управления, и его основное применение - программирование пользовательских интерфейсов, в том числе таких, которые могут динамически изменяться в процессе использования. Это значительная альтернатива парадигме Model-View-Controller.

Я упоминаю об этом, потому что можно подумать, что такой код будет сильно полагаться на закрытия и сбор мусора, но побочным эффектом структуры управления является то, что она устраняет и то, и другое, по крайней мере, в коде пользовательского интерфейса.

1
ответ дан 2 December 2019 в 03:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: