Выполнение кода — итоги
Кратко — что стоит унести из раздела "Выполнение кода". Если пункт кажется туманным — откройте указанную главу или оглавление.
FAQ — Часто задаваемые вопросы
Типичные сбои при переходе от исходника к работающей программе, плюс формулировки, как их ищут в Google — с кратким ответом и ссылкой на главу. Определения для зачёта — в чек-листе.
Вопрос. Программа падает с StackOverflowError или "maximum recursion depth exceeded".
Ответ. Стек вызовов переполнен — обычно бесконечная или слишком глубокая рекурсия без базового случая. Перепишите на цикл или добавьте явное условие выхода и ограничение глубины. Подробнее здесь — вызовы, функции.
Вопрос. OutOfMemoryError, хотя в диспетчере задач "ещё много RAM".
Ответ. Процесс упёрся в лимит кучи или фрагментацию, а не в физическую память целиком. Ищите бесконечное накопление объектов, огромные массивы в памяти и утечки через коллекции. Подробнее здесь — память процесса, переменные.
Вопрос. Локальная переменная в C/C++ показывает "мусор" при первом чтении.
Ответ. На стеке лежит неинициализированное значение — чтение даёт неопределённое поведение. Всегда присваивайте начальное значение перед использованием. Подробнее здесь — неопределённое поведение, переменные.
Вопрос. В отладчике переменная "optimized out" — не вижу значение.
Ответ. Компилятор в Release убрал переменную, оставив только регистры. Соберите Debug, временно пометьте volatile или отключите оптимизацию для проблемного участка. Подробнее здесь — архитектура процессора, отладка.
Вопрос. .exe собран на 64-bit, на старом ПК "не запускается" — связано с разрядностью?
Ответ. 64-битный процесс не стартует на 32-битной ОС; обратная совместимость часто есть, но не гарантирована для нативного кода. Собирайте под целевую платформу AnyCPU/x86/x64 явно. Подробнее здесь — машинное слово, программа.
Вопрос. Массив создал в функции — после return указатель "битый".
Ответ. Память на стеке освобождается при выходе из функции; возвращать адрес локального массива в C нельзя. Выделяйте в куче (malloc, new) или возвращайте копию по значению. Подробнее здесь — память, функции.
Вопрос. Конкatenация строк в цикле на Python/C# стала тормозить на больших данных.
Ответ. Строки неизменяемы — каждый + создаёт новый объект. Используйте StringBuilder, список с одним join или буфер. Подробнее здесь — переменные, оптимизация.
Вопрос. Java-приложение на секунду "замирает" — виноват сборщик мусора?
Ответ. Длинная stop-the-world пауза GC возможна при большой куче или утечке ссылок. Смотрите логи GC, уменьшайте аллокации в горячем пути, проверьте удержание объектов. Подробнее здесь — байт-код и VM, архитектура выполнения.
Вопрос. Скопировал .class или .jar на машину без Java — двойной щелчок ничего не делает.
Ответ. Байт-код исполняет JVM; без установленного JRE или bundled runtime файлы не запустятся. Используйте java -jar из терминала или поставьте JRE. Подробнее здесь — байт-код.
Вопрос. Путаю .NET Framework, .NET Core и просто ".NET" — runtime не находится.
Ответ. Это разные поколения платформы с разными установщиками. Смотрите TargetFramework в проекте и ставьте matching SDK/runtime на машину запуска. Подробнее здесь — байт-код, проект.
Вопрос. Декомпилировал .exe, восстановленный код не компилируется.
Ответ. Декомпиляция теряет имена, типы и структуру; оптимизации стирают "читаемость". Это инструмент анализа, а не кнопка "вернуть исходник". Подробнее здесь — дизассемблирование.
Вопрос. В Debug всё работает, в Release падает без понятной ошибки.
Ответ. Классика неопределённого поведения — выход за границы массива, гонка, чтение освобождённой памяти. Оптимизация меняет порядок операций и "проявляет" баг. Подробнее здесь — неопределённое поведение.
Вопрос. sizeof структуры больше суммы полей — откуда лишние байты?
Ответ. Компилятор вставляет padding для выравнивания под размер машинного слова — так CPU читает быстрее. Менять pack без нужды ломает бинарную совместимость. Подробнее здесь — расположение в памяти, машинное слово.
Вопрос. Бинарный файл, записанный на одном компьютере, на другом читается "задом наперёд".
Ответ. Разный порядок байтов (endianness) — little-endian vs big-endian. Для сетевых форматов и файлов фиксируйте порядок явно или используйте текстовые протоколы. Подробнее здесь — машинное слово, шестнадцатеричная система.
Вопрос. Глобальная переменная меняется "сама" — я её нигде не трогал.
Ответ. Другой модуль, поток или неинициализированное чтение могли изменить значение. Минимизируйте глобальное состояние, ищите все записи через поиск по проекту. Подробнее здесь — переменные.
Вопрос. Замыкание в цикле всегда возвращает последнее значение счётчика.
Ответ. Лямбда захватывает ссылку на переменную, а не снимок на момент создания. Создайте локальную копию внутри тела цикла или используйте default-аргумент. Подробнее здесь — переменные, функции.
Вопрос. Написал if (x = 5) вместо if (x == 5) — программа ведёт себя странно.
Ответ. В C-подобных языках это присваивание, результат всегда "истина". Включите предупреждения компилятора и используйте Yoda-стиль или === там, где язык различает типы. Подробнее здесь — условия.
Вопрос. Цикл for выполнился на одну итерацию больше, чем я считал на бумаге.
Ответ. Проверьте границу: < или <=, off-by-one при индексации с нуля и изменение счётчика внутри тела. Подробнее здесь — циклы.
Вопрос. JVM выдаёт VerifyError при запуске чужого .class.
Ответ. Байт-код не прошёл верификацию — повреждённый файл, несовместимая версия class file или подмена после компиляции. Пересоберите из исходников той же версии JDK. Подробнее здесь — байт-код.
Вопрос. Первый запуск Java/.NET медленный, следующие быстрее — это нормально?
Ответ. Да, JIT прогревает горячие методы после интерпретации. Для бенчмарков делайте несколько прогонов и не сравнивайте "холодный" старт с уже скомпилированным AOT. Подробнее здесь — байт-код.
Вопрос. Стек вызовов в логе ошибки — десятки непонятных строк, с чего начать?
Ответ. Читайте сверху вниз до первого вашего файла — там место, где исключение "всплыло". Ниже — цепочка вызовов, которая к нему привела. Подробнее здесь — функции, ошибки.
Вопрос. Добавил одну глобальную переменную — программа стала вдвое медленнее. Совпадение?
Ответ. Глобальные данные хуже ложатся в кэш CPU и мешают оптимизациям; иногда включается лишняя синхронизация. Профилируйте, прежде чем обвинять одну строку. Подробнее здесь — регистры, архитектура.
Вопрос. Hex-дамп в отладчике выглядит как случайные числа — как им пользоваться?
Ответ. Память — байты под тип данных; учите представление int, pointer и строк в hex параллельно с шестнадцатеричной системой. Сверяйте адрес и размер поля со структурой. Подробнее здесь — шестнадцатеричная система, битовые операции.
Вопрос. AOT-сборка (Native AOT, GraalVM) не находит reflection-классы в runtime.
Ответ. AOT включает только статически известный код; динамическая загрузка требует конфигурации трассировки. Либо оставьте JIT, либо явно зарегистрируйте типы для AOT. Подробнее здесь — байт-код.
Вопрос. Процесс "ест" всю RAM, хотя объектов в коде немного.
Ответ. Проверьте подкачку и виртуальную память, нативные буферы (изображения, mmap), утечки в C-библиотеках через P/Invoke. Снимите heap dump или используйте perf/monitors. Подробнее здесь — память процесса, метрики.
Вопрос. Не понимаю разницу между "программа на диске" и "процесс в памяти".
Ответ. Файл .exe — статический образ; после запуска ОС создаёт процесс с сегментами кода, данных, стеком и кучей в RAM. Один файл может породить несколько процессов. Подробнее здесь — выполнение, программа.
Вопрос. Функция "чистая" на бумаге, но результат разный при одинаковом входе.
Ответ. Скрытые зависимости — глобальное состояние, время, I/O, random. Чистая функция зависит только от аргументов; вынесите побочные эффекты наружу. Подробнее здесь — функции.
Вопрос. Как компьютер выполняет программу пошагово?
Ответ. Цикл fetch → decode → execute: процессор читает инструкцию из памяти, расшифровывает и меняет регистры/память; так до конца процесса. Подробнее здесь — выполнение, архитектура CPU.
Вопрос. Что такое стек и куча (heap) в программировании?
Ответ. Стек — локальные переменные и вызовы функций LIFO; куча — динамические объекты с неограниченным временем жизни. Подробнее здесь — память, архитектура.
Вопрос. Чем стек отличается от кучи?
Ответ. Стек автоматически освобождается при выходе из функции; куча требует free/delete или сборщика мусора. Стек быстрее и ограничен по размеру. Подробнее здесь — память.
Вопрос. Что такое стек вызовов (call stack)?
Ответ. Цепочка активных функций от main до текущей — в логе ошибки читайте сверху вниз до своего файла. Подробнее здесь — процесс выполнения, функции.
Вопрос. Что такое JVM и зачем нужна Java Virtual Machine?
Ответ. JVM исполняет байт-код .class на любой ОС с установленной машиной; JIT ускоряет горячие методы. Подробнее здесь — байт-код.
Вопрос. Что такое .NET runtime CLR?
Ответ. Среда выполнения для C#, F#, VB.NET — загрузка сборок, JIT, GC, безопасность типов. Аналог JVM для экосистемы Microsoft. Подробнее здесь — байт-код.
Вопрос. Что такое JIT-компиляция простыми словами?
Ответ. Компиляция во время работы программы — часто используемый байт-код переводят в машинный код для скорости. Подробнее здесь — байт-код, теория кода.
Вопрос. Что такое AOT-компиляция?
Ответ. Ahead-of-time — машинный код до запуска, без JIT на клиенте; быстрый старт, меньше surprise в prod. Подробнее здесь — байт-код.
Вопрос. Чем компилятор отличается от интерпретатора?
Ответ. Компилятор заранее строит исполняемый файл или байт-код; интерпретатор читает строки при каждом запуске (или смешанный JIT). Подробнее здесь — теория, выполнение.
Вопрос. Что такое garbage collection (сборка мусора)?
Ответ. Runtime освобождает объекты в куче, на которые не осталось ссылок — Java, C#, Python, Go. Утечки возможны, если ссылки удерживают объект. Подробнее здесь — байт-код, архитектура.
Вопрос. Что такое segmentation fault (segfault)?
Ответ. Обращение к недопустимому адресу памяти — null pointer, выход за границы массива, use-after-free в C/C++. Подробнее здесь — неопределённое поведение, память.
Вопрос. Что такое undefined behavior в C и C++?
Ответ. Стандарт не гарантирует результат — программа может упасть, дать мусор или "работать" до смены компилятора. Опаснее обычного exception. Подробнее здесь — неопределённое поведение.
Вопрос. Что такое регистры процессора?
Ответ. Сверхбыстрая память внутри CPU для операндов, адресов и флагов — быстрее RAM и кэша. Подробнее здесь — регистры.
Вопрос. 32-bit или 64-bit программа — в чём разница?
Ответ. Размер машинного слова и адресного пространства — 32-bit до ~4 ГБ RAM на процесс, 64-bit — больше и другой ABI. Подробнее здесь — машинное слово.
Вопрос. Что такое виртуальная память?
Ответ. ОС даёт процессу своё адресное пространство; страницы в RAM или на диске (swap). Изоляция процессов и больше "логической" памяти, чем физической. Подробнее здесь — память процесса.
Вопрос. Что такое декомпиляция программы?
Ответ. Восстановление похожего на исходник текста из .exe или байт-кода — для анализа, не для точной копии. Подробнее здесь — дизассемблирование.
Вопрос. Что такое машинное слово?
Ответ. Единица данных, с которой CPU удобно работает за такт — 32 или 64 бита; влияет на адресацию и размер регистров. Подробнее здесь — машинное слово.
Вопрос. Что такое endianness (порядок байтов)?
Ответ. Порядок байтов в многобайтовом числе — little-endian или big-endian; важен при сетевых протоколах и бинарных файлах. Подробнее здесь — машинное слово.
Вопрос. Как работает return в функции?
Ответ. Завершает функцию, кладёт результат (если есть) и восстанавливает предыдущий кадр стека. Подробнее здесь — функции, процесс выполнения.
Вопрос. Что такое рекурсия и почему stack overflow?
Ответ. Функция вызывает сама себя; каждый вызов — новый кадр на стеке. Без базового случая стек переполняется. Подробнее здесь — функции, вызовы.
Вопрос. Как Python хранит переменные в памяти?
Ответ. Имена — ссылки на объекты в куче; присваивание меняет ссылку, immutable-типы создают новый объект. Подробнее здесь — переменные, Python.
Что запомнить
Выполнение программного кода — это многоуровневый процесс, начинающийся с абстрактного описания логики на языке высокого уровня и завершающийся физическими изменениями состояния транзисторов в микросхемах процессора. Этот путь проходит через этапы компиляции или интерпретации, загрузки в среду выполнения, управления памятью, работы с регистрами и кэшем, а также взаимодействия с операционной системой и аппаратным обеспечением.
Ключевыми элементами этого процесса являются:
- Переменные — именованные ссылки на участки памяти, имеющие ограниченное время жизни и область видимости.
- Функции — изолированные блоки кода, принимающие входные данные, обрабатывающие их и возвращающие результат, при этом управляя потоком выполнения через стек вызовов.
- Циклы — механизмы повторного выполнения инструкций, реализуемые через условные переходы и изменение состояния переменных.
- Условные операторы — точки ветвления, определяющие дальнейший путь выполнения программы на основе логических выражений.
- Машинное слово — базовая единица данных, с которой эффективно работает процессор, определяющая разрядность адресов, регистров и вычислений.
- Регистры — сверхбыстрые ячейки внутри CPU, используемые для временного хранения операндов, адресов и управляющей информации.
- Память — иерархическая система, включающая регистры, кэш, оперативную память и внешние носители, где данные организованы в стек и кучу.
- Байт-код и виртуальные машины — промежуточный уровень абстракции, обеспечивающий платформонезависимость и гибкость исполнения.
- Дизассемблирование и декомпиляция — методы анализа исполняемых файлов, позволяющие восстановить логику программы без исходного кода.
- Неопределённое поведение — ситуация, когда спецификация языка не гарантирует результат выполнения, что делает программу непредсказуемой и потенциально опасной.
Понимание этих механизмов позволяет писать не только корректный, но и эффективный, безопасный и переносимый код. Оно формирует основу системного мышления, необходимого для работы с производительностью, отладкой, низкоуровневыми API, встраиваемыми системами и критически важными приложениями.
Куда идти дальше
| Тема | Раздел |
|---|---|
| "Код — о разделе" | "Код — о разделе" |
| "Проект, структура и фреймворки — о разделе" | "Проект, структура и фреймворки — о разделе" |
| "Алгоритмы — о разделе" | "Алгоритмы — о разделе" |
| "Асинхронность — о разделе" | "Асинхронность — о разделе" |
Проверьте себя: Чек-лист самопроверки.