Долгое время я думал, что C ++ (STL) будет выбрасывать bad_alloc
, когда не было доступной памяти.
Однако, руководствуясь некоторыми общими знаниями, которые я слышал о Linux (например, «Linux на самом деле не резервирует память, пока вы ее не используете»), я решил проверить, как это повлияло на поведение bad_alloc
.
Оказывается, есть определенно определенные применения, в которых bad_alloc
не генерируется, потому что фактическая ошибка происходит после того, как allocator
выполнил свою работу.
В этом примере в первом цикле я выделяю явно больше памяти (1 ТБ)
чем то, что у меня есть в моей системе Linux Fedora30.
Этот цикл заканчивается, и следующий цикл выполняется примерно до тех пор, пока я не инициализирую (создаю) около 100 ГБ (= общий объем ОЗУ + обмен в моей системе).
#include<iostream>
#include<memory>
#include<cassert>
#include<vector>
using T = char;
int main(){
std::size_t block_size = 1000000000; // ~1GB
std::size_t n_blocks = 1000; // number of blocks
std::allocator<T> A;
std::vector<T*> ps(n_block);
for(int i = 0; i != n_block; ++i){
cout << "allocating block " << i << std::endl;
ps[i] = A.allocate(block_size); // ps[i] = (char*)malloc(1000000000);
assert(ps[i]);
}
for(int i = 0; i != n_block; ++i){
cout << "constructing block " << i << std::endl;
for(long j = 0; j != block_size; ++j){
A.construct(ps[i] + j, 'z'); // ps[i][j] = 'z'; // hard error "Killed" HERE
}
}
//////////////////////////////// interesting part ends here
for(int i = 0; i != n_block; ++i){
for(long j = 0; j != block_size; ++j){
assert(ps[i][j] == 'z');
A.destroy(ps[i]);
}
A.deallocate(ps[i], block_size);
}
}
Я понимаю, что в операционных системах существуют идиосинкразии и что во многих операциях, связанных с системой, наблюдается неопределенное поведение.
У меня вопрос: правильно ли я использую C ++? Нужно ли что-то делать с этим поведением с точки зрения восстановления ожидаемого поведения bad_alloc
?
Даже если нет, то есть ли способ обнаружить, что к какой-то памяти нельзя прикоснуться заранее? Проверка на null
, кажется, не покрывает этот случай (опять же, в Linux.)
Когда я строил этот пример, я думал, что , возможно, , construct
(то есть размещение new
внутри него) как-то сгенерирует или даст какую-то ошибку при обнаружении чего-то подозрительного в расположении необработанного указателя. но этого не произошло: Linux просто «убил» программу.
Это вывод этой программы в Fedora30 с подкачкой 32 ГБ + 64 ГБ:
1
2
3
...
997
998
999
*0
*1
*2
*3
...
*86
*87
*88
*89
*90
Killed
(Linux выводит «Killed» и выходит из программы).
Примечание: я знаю другие варианты использования (например, размеры блоков, другой порядок - чередование - размещение и построение и т. Д.), В которых программа выдает bad_alloc
. Я спрашиваю конкретно об использовании, подобном этому, и есть ли способ восстановления в этом контексте.
Например, я знаю, что если я сделаю A.allocate("1TB")
, это сразу выбросит bad_alloc
.
Это также произойдет изящно, если я чередую выделение конструкции из небольших блоков:
for(int i = 0; i != n_block; ++i){
ps[i] = A.allocate(block_size); //eventually throws, but HERE
for(long j = 0; j != block_size; ++j){
A.construct(ps[i] + j, 'z');
}
}