Страницы

Поиск по вопросам

Показаны сообщения с ярлыком stack. Показать все сообщения
Показаны сообщения с ярлыком stack. Показать все сообщения

суббота, 7 марта 2020 г.

Как вытаскивать из стека нужные значения?

#ассемблер #stack


Я сначала сохраняю PUSH EAX, потом PUSH EAX, то есть значения двух регистров. Возможно,
есть специальная инструкция, которая позволяет взять самое нижнее значение стэка? Вершиной
стэка - значение EAX, дно стэка значение ECX. Как взять значение из "дна", изменить
его и положить обратно на дно?
Благодарю за ответы!
    


Ответы

Ответ 1



Машинный стек - это просто участок памяти, с ним можно работать как с любым другим участком памяти, например с помощью команды mov можно обратиться к любому адресу выше текущей вершины (адреса, лежащего в esp). Действия из вопроса можно выполнить так: push eax push ecx mov edx, [esp+4] ; берем значение, которое было добавлено в стек предпоследним ... ; как-то меняем mov [esp+4], edx ; кладем обратно Дном стека для процедуры условно можно считать значение указателя стека, которое он имеет в момент входа в процедуру. Есть стандартный прием, когда это значение сохраняется в регистр ebp, потом в стеке выделяется память под локальные переменные (указатель стека просто уменьшается на размер локальных переменных), а потом в процессе работы процедуры от ebp в большую сторону ([ebp+N]) рассчитываются адреса аргументов процедуры, а в меньшую сторону ([ebp-N]) - адреса локальных переменных. При этом значение esp в процессе работы процедуры может меняться, а значение ebp остается одним и тем же. Код процедуры будет выглядеть примерно так: ; Стандартный пролог для создания стекового фрейма push ebp mov ebp, esp sub esp, M ; M - размер локальных переменных ... ; основной код процедуры ; Стандартный эпилог mov esp, ebp pop ebp ret

вторник, 18 февраля 2020 г.

Полный путь к точке выброса исключения

#java #stack


Как избавиться от "... 1 more" и увидеть полный стек вызовов, приведших к выбросу
исключения, если результатом выводится только это:

// other output data
myPack.DynamicFieldsException
    at myPack.DynamicFields.setField(DynamicFields.java:69)
    at myPack.DynamicFields.main(DynamicFields.java:99)
Caused by: java.lang.NullPointerException
    at myPack.DynamicFields.setField(DynamicFields.java:70)
    ... 1 more


Просьба в ответе показать конкретный пример, т.е. 

catch (MyException e) { ??? }


Какой код следует использовать вместо "???" чтобы получить желаемый результат (полный
вывод стека) ?

Сейчас используется:

catch (MyException e) { e.printStackTrace(); }


Такой вариант:

catch (NoSuchFieldException e) {
        for (StackTraceElement l : e.getStackTrace())
            System.out.println(l);
}


не показывает причину, заранее заданную как new Exception(new Exception()); или как
new MyException().initCause(new MyException()); 
    


Ответы

Ответ 1



Этот способ вывода информации об исключении, является способом по умолчанию. Вот так это выглядит в коде if (framesInCommon != 0) s.println(prefix + "\t... " + framesInCommon + " more"); Но вы всегда можете перехватить исключение и вывести его в нужном вам формате. Метод getStackTrace вернет вам массив типа StackTraceElement. В нем вы найдете всю необходимую информацию о стеке. try { throw new Exception(new NullPointerException()); } catch (Exception ex) { Throwable cause = ex; while (cause != null) { for(StackTraceElement element: cause.getStackTrace()) { System.out.println(element.toString()); } cause = cause.getCause(); if(cause != null) { System.out.println("Caused by " + cause); } } }

воскресенье, 16 февраля 2020 г.

Полный путь к точке выброса исключения

#java #stack


Как избавиться от "... 1 more" и увидеть полный стек вызовов, приведших к выбросу
исключения, если результатом выводится только это:

// other output data
myPack.DynamicFieldsException
    at myPack.DynamicFields.setField(DynamicFields.java:69)
    at myPack.DynamicFields.main(DynamicFields.java:99)
Caused by: java.lang.NullPointerException
    at myPack.DynamicFields.setField(DynamicFields.java:70)
    ... 1 more


Просьба в ответе показать конкретный пример, т.е. 

catch (MyException e) { ??? }


Какой код следует использовать вместо "???" чтобы получить желаемый результат (полный
вывод стека) ?

Сейчас используется:

catch (MyException e) { e.printStackTrace(); }


Такой вариант:

catch (NoSuchFieldException e) {
        for (StackTraceElement l : e.getStackTrace())
            System.out.println(l);
}


не показывает причину, заранее заданную как new Exception(new Exception()); или как
new MyException().initCause(new MyException()); 
    


Ответы

Ответ 1



Этот способ вывода информации об исключении, является способом по умолчанию. Вот так это выглядит в коде if (framesInCommon != 0) s.println(prefix + "\t... " + framesInCommon + " more"); Но вы всегда можете перехватить исключение и вывести его в нужном вам формате. Метод getStackTrace вернет вам массив типа StackTraceElement. В нем вы найдете всю необходимую информацию о стеке. try { throw new Exception(new NullPointerException()); } catch (Exception ex) { Throwable cause = ex; while (cause != null) { for(StackTraceElement element: cause.getStackTrace()) { System.out.println(element.toString()); } cause = cause.getCause(); if(cause != null) { System.out.println("Caused by " + cause); } } }

понедельник, 10 февраля 2020 г.

Удалить конкретное activity из стека

#android #activity #stack


Как удалить конкретное activity из стека? вот пример, есть  activty A,B,C, пользователь
переходит A->B->C, и в activty C в определенных условиях, 


надо вернуться назад(activty B) (делаю finish)
надо вернуться назад(сразу в activty A минуя activty B, при этом не пересоздавая
activty A, как это сделать?)


//делаю так, он пересоздает activty  A

Intent intent = new Intent(this, A);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

    


Ответы

Ответ 1



Городить код с запуском активити через onActivityResult() - нерационально. Следует назначить активити А android:launchMode="singleTask". И запустив startActivity(new Intent(this, A) активити А вытащится из стека не пересоздаваясь, попутно закрыв верхние активити.

Ответ 2



Ответ на вопросы: Что бы вернуться с активити С на В, ты все делаешь правильно, просто вызываешь finish() у активити С. Что бы вернуться с С на А нужно немного добавить логики. Когда запускаешь активити С, делай это через: startActivtyForResult(new Intent(this, C.class), 1234) При завершении С, перед вызовом finish(), в обратный интент кинь флаг нужно ли завершиться активити В или нет: Intent answerIntent = new Intent(); Bundle bundle = new Bundle(); bundle.putBoolean("need_finish", true); answerIntent.putExtras(bundle); setResult(RESULT_OK, answerIntent); Активити В обрабатывает метод onActivityResult. Если флаг true, то тоже вызываешь finish() у В : onActivityResult(int requestCode, int resultCode, Intent data){ if (requestCode == 1234 && data != null) { if (data.getBoolean("need_finish", true)) { finish(); } } }

среда, 5 февраля 2020 г.

Класс Stack в Java

#java #list #коллекции #stack


Какой класс лучше использовать для использования стека в java и почему он лучше?
    


Ответы

Ответ 1



Deque Для стэка рекомендуется использовать реализации интерфейса Deque. Интерфейс включает методы стэка: push, poll и peek. Стандартные реализации перечислены в документации, например: ArrayDeque — дэк на основе массива; LinkedList — связный список; ConcurrentLinkedDeque — потокобезопасная реализация. Выбор класса зависит от логики приложения (какие операции выполняются и как часто, как изменяется размер стэка) и потребует оценки производительности. В документации к ArrayDeque утверждается, что в качестве очереди он, скорее всего, будет быстрее LinkedList: ... This class is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue. так что, если не требуется доступ из нескольких потоков, разумно будет использовать ArrayDeque по умолчанию. Методы poll и peek в Deque унаследованы от Queue и работают в последовательности для очереди (FIFO). Для симуляции работы стэка можно использовать вместо них методы pollLast и peekLast. Queue Более удобный вариант: с помощью метода Collections.asLifoQueue преобразовать Deque в объект Queue, который ведет себя как стэк (LIFO). В этом случае будут корректно работать все методы и итераторы. Также не будут доступны лишние, не допустимые для стэка операции: Queue stack = Collections.asLifoQueue(new ArrayDeque<>()); stack.addAll(Arrays.asList("a", "b", "c", "d", "e")); //edcba stack.forEach(element -> System.out.println(element)); Stack не рекомендуется использовать Также есть стандартный класс java.util.Stack, но его не рекомендуется использовать. Из документации: A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class. For example: Deque stack = new ArrayDeque(); Более полный и согласованный набор LIFO операций предоставляется через интерфейс Deque и его реализации, которые должны использоваться вместо этого класса. Например: Deque stack = new ArrayDeque(); т.е. класс сохранен для обратной совместимости, но вместо него нужно использовать реализации Deque. Это вызвано историческими причинами: в первой версии Java в реализации Stack допущены ошибки, например: Stack — конкретный класс, в отличие от остальных базовых коллекций, которые представлены интерфейсами: Set, List, Queue; класс наследуется от Vector, что концептуально неверно (поддерживаются лишние операции). Подробнее о проблемах Stack: Why Stack extends Vector in JDK? What are the negative aspects of Java class Stack inheriting from Vector?

Ответ 2



Мне кажется стоит использовать ArrayList или LinkedList. Второй будет использовать больше памяти, но зато операция добавления элемента в нём работает честные O(1), в отличии от ArrayList, у которого O(1) амортизированные. Не следует использовать класс Stack, потому что в нём все методы помечены как synchronized, что замедляет производительность в случае, если ваш стек использует только один поток (почти всегда именно так и происходит).

Ответ 3



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

суббота, 11 января 2020 г.

textbox.Text=stack.Pop() не работает

#c_sharp #stack


Каким образом работает stack.pop() ?
Хочу реализовать функцию Undo

Записую в Stack текст с формы при изменении текста

private void Output_tb_TextChanged(object sender, EventArgs e)
{
    undoActions.Push(output_tb.Text);
}


Здесь возвращаю текст при нажатии кнопки Undo

private void Undo_bt_Click(object sender, EventArgs e)
    {
        if (undoActions.Count<1)
        {
            return;
        }

        output_tb.Text = undoActions.Pop();


Но прежнее состояние текста появляется только при втором нажатии на кнопку Undo.
Почему так происходит?
    


Ответы

Ответ 1



потому что последнее значение в стэке, такое же, как текущее значение в текстбоксе if (output_tb.Text != undoActions.Peek()) undoActions.Push(output_tb.Text);

пятница, 10 января 2020 г.

Когда уничтожается ValueType и семантика работы GC с ValueType

#net #память #сборщик_мусора #clr #stack


Ходят мифы и легенды,мол ValueType удаляется посредством GC(то бишь GC деаллоцирует
как ReferenceType,так и ValueType).
Но на самом то деле это не так. К примеру у нас есть код:  

class A {}
class B
{
    void TestMethod()
    {
        A a = new A();
        int x = 100;
    }
}  


в контексте(Scope) метода TestMethod(), создается объект(ReferenceType) типа А,а
так же переменная X(ValueType).   

По завершению работы метода, переменная X уничтожается,а объект типа А теряет ссылку
на объект,и становится претендентом для удаления от GC.  

Иными словами,ValueTypе существует в контексте до тех пор,пока выполняется,и соответственно
Stack, по типу метода Pop() сам удалит эти данные из памяти, и никакого участия в этом
не принимает GC, поэтому ValueType и работает быстрее (хотя все зависит от задачи).   

И сам вопрос,всегда ли это так работает? (читал разные статьи,иногда пишут,что это
происходит только тогда,когда стек забивается, т.е. доходит до заполнения)   

Что делает CLR,когда стек уже почти переполнен,а все данные в нем к примеру являются
ссылками на объекты в куче?
Как и когда удаляются пользовательские структуры? Как именно CLR решает удалять ли
данные из стека или оставить их еще существовать N-ое кол-во времени!??
    


Ответы

Ответ 1



Локальные переменные типов ValueType [на которых нет замыканий из анонимных методов и лябмд] лежат прямо в стеке или в регистрах процессора (как захочется оптимизатору). Вы можете прямо посмотреть, как выполняется ваш код, нажав правой кнопкой по нему в отладке, и выбрав Go To Disassembly, может быть это прояснит картину. Вот как это выглядит в отладочном режиме (что выключает оптимизации). Я добавил комментарии в важных местах: { 025B2E48 push ebp // это так называемый пролог функции 025B2E49 mov ebp,esp // https://en.wikipedia.org/wiki/Function_prologue 025B2E4B push edi // суть его - сохранить текущее положение 025B2E4C push esi // стека в "базовый указатель" - [e]bp 025B2E4D push ebx в стек запихнули значения 3-х регистров, так что его указатель теперь отличается уже на 12 от того, который был в начале функции 025B2E4E sub esp,3Ch esp - это указатель на начало стека. Уменьшить его на 3Ch - это выделить в стеке 3Сh (60) байт под локальные переменные (или другие накладные расходы) к этому моменту он уже отличался на 12 от значения, которое лежит в ebp, так что локальные переменные находятся в диапазоне по адресам от [ebp-13] до [ebp-72]. Он же [ebp-0Dh] до [ebp-48h]. Потом делаем кучу проверок и долго и мучительно создаем объект (это все из-за отладочного режима). Я пропущу большую часть кода, она не имеет отношения к вопросу: 025B2E51 mov esi,ecx 025B2E7C nop A a = new A(); 025B2E7D mov ecx,700F98h 025B2E82 call 024130F4 025B2E87 mov dword ptr [ebp-48h],eax 025B2E8A mov ecx,dword ptr [ebp-48h] 025B2E8D call 025B0D18 025B2E92 mov eax,dword ptr [ebp-48h] и вот наконец ложим указатель на созданный объект в стек (в ebp лежит положение стека на момент начала вызова функции) 025B2E95 mov dword ptr [ebp-40h],eax С целым числом попроще - просто запихиваем нужное значение в относительно epb - т.е. относительно начала стека на момент функции. int x = 100; 025B2E98 mov dword ptr [ebp-44h],64h } 025B2E9F nop А вот теперь фокус. Берем и загружаем в указатель стека значение, которое в нем было сразу после 025B2E4D push ebx. По сути это esp = ebp-0Ch 025B2EA0 lea esp,[ebp-0Ch] 025B2EA3 pop ebx 025B2EA4 pop esi 025B2EA5 pop edi и после следующей строчки получаем значение esp равное тому, которое было в начале функции. 025B2EA6 pop ebp 025B2EA7 ret За счет чего при этом выделалась и освобождалась память в стеке? Выделалась за счет уменьшения указателя стека на нужное значение. Освобождалась - за счет восстановления старого значения указателя. Расходов на разрушение или "сброрку мусора" локальных переменных при этом не было. Это стандартный механизм на x86, так что можно считать что так происходит почти всегда. По возврату из функции значение Stack Pointer восстанавливается в то, что было до ее вызова.

пятница, 20 декабря 2019 г.

Шаблоны C++ , undefined reference to

#cpp #шаблоны_с++ #stack


stack.cpp

//stack.cpp
#include "stack.h"

template 
Stack::Stack()
{
    top = nullptr;
}

template 
void Stack::push(T arg)
{
    Node* temp = new Node;
    temp->value = arg;
    temp->next = top;
    top = temp;
}

template 
T Stack::pop()
{
    if (top != nullptr)
    {
        T result = top->value;
        Node* temp = top;
        top = top->next;
        delete temp;
        return result;
    } else
        return 0;
}

template 
Stack::~Stack()
{
    while (top != nullptr)
        pop();
}


stack.h

//stack.h
#ifndef PROJECT_STACK_H
#define PROJECT_STACK_H

template 
class Stack
{
private:
    struct Node
    {
        T value;
        Node* next;
    };
    Node* top;
public:
    Stack();
    void push(T arg);
    T pop();
    ~Stack();
};


#endif //PROJECT_STACK_H


//main.cpp примерно следующего содержания:

Stack  s;
s.push('a');


компилирую через g++ так: g++ -std=c++11 main.cpp stack.cpp

Получаю следующие ошибки:

/tmp/ccvtzAEt.o:main.cpp:(.text+0x1b): undefined reference to `Stack::Stack()'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x1b): relocation truncated to fit: R_X86_64_PC32
against undefined symbol `Stack::Stack()'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x2c): undefined reference to `Stack::push(char)'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x2c): relocation truncated to fit: R_X86_64_PC32
against undefined symbol `Stack::push(char)'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x3d): undefined reference to `Stack::push(char)'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x3d): relocation truncated to fit: R_X86_64_PC32
against undefined symbol `Stack::push(char)'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x49): undefined reference to `Stack::pop()'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x49): relocation truncated to fit: R_X86_64_PC32
against undefined symbol `Stack::pop()'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x66): undefined reference to `Stack::pop()'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x66): relocation truncated to fit: R_X86_64_PC32
against undefined symbol `Stack::pop()'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x9b): undefined reference to `Stack::~Stack()'
/tmp/ccvtzAEt.o:main.cpp:(.text+0x9b): relocation truncated to fit: R_X86_64_PC32
against undefined symbol `Stack::~Stack()'
/tmp/ccvtzAEt.o:main.cpp:(.text+0xae): undefined reference to `Stack::~Stack()'
/tmp/ccvtzAEt.o:main.cpp:(.text+0xae): relocation truncated to fit: R_X86_64_PC32
against undefined symbol `Stack::~Stack()'
collect2: ошибка: выполнение ld завершилось с кодом возврата 1


То есть  undefined reference to Stack::Stack() и undefined reference to Stack::push(char)
и undefined reference to Stack::~Stack()

В чем проблема? Заранее огромное спасибо!
    


Ответы

Ответ 1



Ну откуда же при компиляции stack.cpp компилятору знать, что где-то, в совсем другом файле вам понадобится Stack? Не выносите реализацию шаблонов в отдельный файл, оставляйте ее в заголовочном файле.

Ответ 2



Либо не выносите определения шаблонов в отдельный файл (т.е. реализуйте все в заголовочном файле), либо не забывайте включать этот отдельный файл повсеместно, как включаются обычные заголовочные файлы. На практике у обоих подходов есть свои поклонники. Но при этом давать файлу реализации расширение .cpp не принято - это все таки включаемый, а не отдельно компилируемый файл. Скармливать ваш файл реализации непосредственно компилятору (g++ stack.cpp) бесполезно: определения шаблонов не порождают кода, в чем вы сможете убедиться взглянув на сгенерированный из stack.cpp объектный файл - он будет пуст. P.S. Теоретически, вы можете оставить все как есть, но добавить в stack.cpp явные инстанциации вашего шаблона для всех требуемых типов аргументов. Т.е. в вашем случае достаточно добавить в stack.cpp строчку template Stack; и все сразу заработает в оригинальном виде, при компиляции через ваше g++ -std=c++11 main.cpp stack.cpp. Но, как вы сами понимаете, в общем случае это - не жизнеспособный подход, а специальная фича, предназначенная совсем для других целей.

среда, 22 мая 2019 г.

Как вытаскивать из стека нужные значения?

Я сначала сохраняю PUSH EAX, потом PUSH EAX, то есть значения двух регистров. Возможно, есть специальная инструкция, которая позволяет взять самое нижнее значение стэка? Вершиной стэка - значение EAX, дно стэка значение ECX. Как взять значение из "дна", изменить его и положить обратно на дно? Благодарю за ответы!


Ответ

Машинный стек - это просто участок памяти, с ним можно работать как с любым другим участком памяти, например с помощью команды mov можно обратиться к любому адресу выше текущей вершины (адреса, лежащего в esp).
Действия из вопроса можно выполнить так:
push eax push ecx mov edx, [esp+4] ; берем значение, которое было добавлено в стек предпоследним ... ; как-то меняем mov [esp+4], edx ; кладем обратно
Дном стека для процедуры условно можно считать значение указателя стека, которое он имеет в момент входа в процедуру. Есть стандартный прием, когда это значение сохраняется в регистр ebp, потом в стеке выделяется память под локальные переменные (указатель стека просто уменьшается на размер локальных переменных), а потом в процессе работы процедуры от ebp в большую сторону ([ebp+N]) рассчитываются адреса аргументов процедуры, а в меньшую сторону ([ebp-N]) - адреса локальных переменных. При этом значение esp в процессе работы процедуры может меняться, а значение ebp остается одним и тем же. Код процедуры будет выглядеть примерно так:
; Стандартный пролог для создания стекового фрейма push ebp mov ebp, esp sub esp, M ; M - размер локальных переменных
... ; основной код процедуры
; Стандартный эпилог mov esp, ebp pop ebp ret

пятница, 26 апреля 2019 г.

Удалить конкретное activity из стека

Как удалить конкретное activity из стека? вот пример, есть activty A,B,C, пользователь переходит A->B->C, и в activty C в определенных условиях,
надо вернуться назад(activty B) (делаю finish) надо вернуться назад(сразу в activty A минуя activty B, при этом не пересоздавая activty A, как это сделать?)
//делаю так, он пересоздает activty A
Intent intent = new Intent(this, A); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent);


Ответ

Городить код с запуском активити через onActivityResult() - нерационально. Следует назначить активити А android:launchMode="singleTask". И запустив startActivity(new Intent(this, A) активити А вытащится из стека не пересоздаваясь, попутно закрыв верхние активити.

Полный путь к точке выброса исключения

Как избавиться от "... 1 more" и увидеть полный стек вызовов, приведших к выбросу исключения, если результатом выводится только это:
// other output data myPack.DynamicFieldsException at myPack.DynamicFields.setField(DynamicFields.java:69) at myPack.DynamicFields.main(DynamicFields.java:99) Caused by: java.lang.NullPointerException at myPack.DynamicFields.setField(DynamicFields.java:70) ... 1 more
Просьба в ответе показать конкретный пример, т.е.
catch (MyException e) { ??? }
Какой код следует использовать вместо "???" чтобы получить желаемый результат (полный вывод стека) ?
Сейчас используется:
catch (MyException e) { e.printStackTrace(); }
Такой вариант:
catch (NoSuchFieldException e) { for (StackTraceElement l : e.getStackTrace()) System.out.println(l); }
не показывает причину, заранее заданную как new Exception(new Exception()); или как new MyException().initCause(new MyException());


Ответ

Этот способ вывода информации об исключении, является способом по умолчанию. Вот так это выглядит в коде
if (framesInCommon != 0) s.println(prefix + "\t... " + framesInCommon + " more");
Но вы всегда можете перехватить исключение и вывести его в нужном вам формате. Метод getStackTrace вернет вам массив типа StackTraceElement. В нем вы найдете всю необходимую информацию о стеке.
try { throw new Exception(new NullPointerException()); } catch (Exception ex) { Throwable cause = ex; while (cause != null) { for(StackTraceElement element: cause.getStackTrace()) { System.out.println(element.toString()); } cause = cause.getCause(); if(cause != null) { System.out.println("Caused by " + cause); } } }

среда, 20 февраля 2019 г.

Когда уничтожается ValueType и семантика работы GC с ValueType

Ходят мифы и легенды,мол ValueType удаляется посредством GC(то бишь GC деаллоцирует как ReferenceType,так и ValueType). Но на самом то деле это не так. К примеру у нас есть код:
class A {} class B { void TestMethod() { A a = new A(); int x = 100; } }
в контексте(Scope) метода TestMethod(), создается объект(ReferenceType) типа А,а так же переменная X(ValueType).
По завершению работы метода, переменная X уничтожается,а объект типа А теряет ссылку на объект,и становится претендентом для удаления от GC.
Иными словами,ValueTypе существует в контексте до тех пор,пока выполняется,и соответственно Stack, по типу метода Pop() сам удалит эти данные из памяти, и никакого участия в этом не принимает GC, поэтому ValueType и работает быстрее (хотя все зависит от задачи).
И сам вопрос,всегда ли это так работает? (читал разные статьи,иногда пишут,что это происходит только тогда,когда стек забивается, т.е. доходит до заполнения)
Что делает CLR,когда стек уже почти переполнен,а все данные в нем к примеру являются ссылками на объекты в куче? Как и когда удаляются пользовательские структуры? Как именно CLR решает удалять ли данные из стека или оставить их еще существовать N-ое кол-во времени!??


Ответ

Локальные переменные типов ValueType [на которых нет замыканий из анонимных методов и лябмд] лежат прямо в стеке или в регистрах процессора (как захочется оптимизатору).
Вы можете прямо посмотреть, как выполняется ваш код, нажав правой кнопкой по нему в отладке, и выбрав Go To Disassembly, может быть это прояснит картину. Вот как это выглядит в отладочном режиме (что выключает оптимизации). Я добавил комментарии в важных местах:
{ 025B2E48 push ebp // это так называемый пролог функции 025B2E49 mov ebp,esp // https://en.wikipedia.org/wiki/Function_prologue 025B2E4B push edi // суть его - сохранить текущее положение 025B2E4C push esi // стека в "базовый указатель" - [e]bp 025B2E4D push ebx
в стек запихнули значения 3-х регистров, так что его указатель теперь отличается уже на 12 от того, который был в начале функции
025B2E4E sub esp,3Ch
esp - это указатель на начало стека. Уменьшить его на 3Ch - это выделить в стеке 3Сh (60) байт под локальные переменные (или другие накладные расходы) к этому моменту он уже отличался на 12 от значения, которое лежит в ebp, так что локальные переменные находятся в диапазоне по адресам от [ebp-13] до [ebp-72]. Он же [ebp-0Dh] до [ebp-48h].
Потом делаем кучу проверок и долго и мучительно создаем объект (это все из-за отладочного режима). Я пропущу большую часть кода, она не имеет отношения к вопросу:
025B2E51 mov esi,ecx 025B2E7C nop A a = new A(); 025B2E7D mov ecx,700F98h 025B2E82 call 024130F4 025B2E87 mov dword ptr [ebp-48h],eax 025B2E8A mov ecx,dword ptr [ebp-48h] 025B2E8D call 025B0D18 025B2E92 mov eax,dword ptr [ebp-48h]
и вот наконец ложим указатель на созданный объект в стек (в ebp лежит положение стека на момент начала вызова функции) 025B2E95 mov dword ptr [ebp-40h],eax
С целым числом попроще - просто запихиваем нужное значение в относительно epb - т.е. относительно начала стека на момент функции.
int x = 100; 025B2E98 mov dword ptr [ebp-44h],64h } 025B2E9F nop
А вот теперь фокус. Берем и загружаем в указатель стека значение, которое в нем было сразу после 025B2E4D push ebx. По сути это esp = ebp-0Ch
025B2EA0 lea esp,[ebp-0Ch] 025B2EA3 pop ebx 025B2EA4 pop esi 025B2EA5 pop edi
и после следующей строчки получаем значение esp равное тому, которое было в начале функции.
025B2EA6 pop ebp 025B2EA7 ret
За счет чего при этом выделалась и освобождалась память в стеке?
Выделалась за счет уменьшения указателя стека на нужное значение. Освобождалась - за счет восстановления старого значения указателя. Расходов на разрушение или "сброрку мусора" локальных переменных при этом не было.
Это стандартный механизм на x86, так что можно считать что так происходит почти всегда. По возврату из функции значение Stack Pointer восстанавливается в то, что было до ее вызова.

среда, 5 декабря 2018 г.

Обнаружить StackOverflowError в других потоках

Я делаю интерпретатор языка Prolog. Язык основан на рекурсии, и неправильно написанные программы часто приводят к переполнению стека. Когда запускал свой интерпретатор из приложения JavaFX я заметил, что JavaFX может обнаруживать StackOverflowError в потоке и обрабатывать его.
Как самому сделать что-то подобное, чтобы определить StackOverflowError, выброшенный в другом потоке и обработать его?


Ответ

В общем случае я бы посоветовал данную задачу (перехват ожидаемого исключения) возложить на сам поток, но если это не возможно по каким либо причинам:
@Override public void start(Stage primaryStage) { Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
См. setDefaultUncaughtExceptionHandler, UncaughtExceptionHandler
Правка (после предоставления исходных кодов):
Внутри установленного UncaughtExceptionHandler
Platform.runLater(() -> errorsOutput.runtimeException(new RuntimeException("Error in program thread", throwable)));