Каждый процесс может использовать память "кучи", чтобы сохранить и обменяться данными в рамках процесса. У нас есть правило в программировании каждый раз, когда мы занимаем некоторое место в памяти "кучи", мы должны выпустить его, после того как задание сделано, еще это приводит к утечкам памяти.
int *pIntPtr = new int;
.
.
.
delete pIntPtr;
Мой вопрос: действительно ли память "кучи" для каждого процесса?
Если ДА,
затем утечка памяти возможна только, когда процесс находится в состоянии выполнения.
Если нет,
затем это означает, что ОС может сохранить данные в памяти где-нибудь. Если так, есть ли способ получить доступ к этой памяти другим процессом. Также это может стать путем к межпроцессному взаимодействию.
Я предполагаю, что ответ на мой вопрос - ДА. Обеспечьте свою ценную обратную связь.
Практически в каждой системе, которая используется в настоящее время, кучная память предназначена для каждого процесса. В старых системах без защищенной памяти динамическая память была общесистемной. (Вкратце, это то, что делает защищенная память : она делает вашу кучу и стек приватными для вашего процесса.)
Итак, в вашем примере кода в любой современной системе, если процесс завершается до delete pIntPtr
вызывается, pIntPtr
все равно будет освобожден (хотя его деструктор, а не то, что у int есть, не будет вызываться.)
Обратите внимание, что защищенная память - это деталь реализации, не функция стандартов C ++ или C. Система может свободно делить память между процессами (современные системы просто этого не делают , потому что это хороший способ дать вам отпор от злоумышленника).
Обычно выделение памяти процессу происходит на более низком уровне, чем управление кучей.
Другими словами, куча создается в виртуальном адресном пространстве процесса, предоставленном ему операционной системой, и является частной для этого процесса. Когда процесс завершается, эта память освобождается операционной системой.
Обратите внимание, что C ++ не требует этого, это часть среды выполнения, в которой работает C ++, поэтому стандарты ISO не требуют такого поведения. Я говорю об общей реализации.
В UNIX системные вызовы brk
и sbrk
использовались для выделения дополнительной памяти из операционной системы для расширения кучи. Затем, когда процесс завершился, вся эта память была возвращена ОС.
Обычный способ получить память, которая может пережить процесс, - это разделяемая память (в операционных системах типа UNIX, не уверен в Windows). Это может привести к утечке, но большего количества системных ресурсов, а не ресурсов процесса.
Обычно операционная система восстанавливает любую утечку памяти при завершении процесса.
По этой причине я считаю, что для программистов на C ++ нормально никогда явно не освобождать память, которая необходима, до тех пор, пока процесс не завершится; например, любые «синглтоны» в процессе часто явно не освобождаются.
Это поведение может быть специфичным для O / S (хотя это верно, например, для Windows и Linux): теоретически не является частью стандарта C ++.
В большинстве современных операционных систем каждый процесс имеет свою собственную кучу, которая доступна только этому процессу и освобождается после завершения процесса - эта "частная" куча обычно используется new
. Также может существовать глобальная куча (например, посмотрите на функции семейства Win32 GlobalAlloc()
), которая разделяется между процессами, сохраняется в течение всего времени работы системы и действительно может использоваться для межпроцессного взаимодействия.
Есть некоторые операционные системы специального назначения, которые не будут восстанавливать память при выходе из процесса. Если вы ориентируетесь на такую ОС, вы, вероятно, знаете.
Большинство систем не позволяют вам получить доступ к памяти другого процесса, но опять же ... есть некоторые уникальные ситуации, когда это не так.
Стандарт C ++ справляется с этой ситуацией, не делая никаких заявлений о том, что произойдет, если вы не освободите память и затем выйдете, а также о том, что произойдет, если вы попытаетесь получить доступ к памяти, доступ к которой не принадлежит вам. Это самая суть того, что означает «неопределенное поведение», и суть того, что означает «недействительный» указатель. Есть больше проблем, чем только эти две, но они играют определенную роль.
Для практических целей ответ на ваш вопрос - да. Современные операционные системы обычно освобождают память, выделенную процессом, когда этот процесс завершается. Однако полагаться на такое поведение - очень низкое занятие. Даже если мы можем быть уверены, что операционные системы всегда будут работать таким образом, код будет хрупким. Если какая-то функция, которая не может освободить память, внезапно будет повторно использована для другой цели, это может привести к утечке памяти на уровне приложения.
Тем не менее, природа этого вопроса и приведенного примера требует, чтобы я с этической точки зрения указал вам и вашей команде на рассмотрение RAII.
int *pIntPtr = new int;
...
delete pIntPtr;
Этот код пахнет утечкой памяти. Если что-то в [...] выкидывает, у вас утечка памяти. Есть несколько решений:
int *pIntPtr = 0;
try
{
pIntPtr = new int;
...
}
catch (...)
{
delete pIntPtr;
throw;
}
delete pIntPtr;
Второе решение с использованием nothrow (не обязательно намного лучше, чем первое, но позволяет разумную инициализацию pIntPtr во время его определения):
int *pIntPtr = new(nothrow) int;
if (pIntPtr)
{
try
{
...
}
catch (...)
{
delete pIntPtr;
throw;
}
delete pIntPtr;
}
И простой способ:
scoped_ptr<int> pIntPtr(new int);
...
В этом последнем и лучшем Например, нет необходимости вызывать delete для pIntPtr, поскольку это делается автоматически независимо от того, как мы выходим из этого блока (ура для RAII и интеллектуальных указателей).