Анимация
JavaScript
|
Главная Библионтека poro диспетчера памяти, а этот диспетчер, в свою очередь, должен решить, как разделить доступную память, опираясь на некоторую стратегию управления памятью. Вот как выглядит краткое описание двух распространенных стратегий управления памятью в С++. Более подробное рассмотрение данного вопроса выходит за рамки нашей книги; дополнительную информацию вы найдете в описании вашей операционной системы. • Распределение общего назначения, или универсальное распределение может обеспечить блок памяти любого размера, который может запросить вызывающая программа (размер запроса, или размер блока). Такое распределение очень гибкое, но имеет ряд недостатков, основными из которых являются пониженная из-за необходимости выполнения большего количества работы производительность и фрагментация памяти, вызванная тем, что при постоянном вьщелении и освобождении блоков памяти разного размера образуется большое количество небольших по размеру несмежных участков свободной памяти. • Распределение фиксированного размера всегда выделяет блоки памяти одного и того же фиксированного размера. Очевидно, что такая стратегия менее гиб-кая, чем универсальная, но зато она работает существенно быстрее и не приводит к фрагментации памяти. Третья важная стратегия, распределение со сборкой мусора, не полностью совместима с указателями С и С+ + , функциями типа mai loc, new и потому не имеет прямого отношения к рассматриваемому нами вопросу. Сборка мусора становится все более популярна и постепенно приходит и в С++ (но не для работы с указателями и оператором new). Я планирую рассмотреть этот вопрос в своей будущей книге, а сейчас вы можете обратиться к [C++CLI04] и [Jones96]". И а практике мы часто сталкиваемся с комбинацией этих стратегий. Например, возможно, ваш диспетчер памяти использует схему общего назначения для всех запросов размером больше некоторого значения б, а в качестве оптимизации для всех запросов размером меньше S используется выделение блоков памяти фиксированного размера. Обычно достаточно неудобно иметь отдельные области памяти для запросов размером 1 байт, 2 байта и так далее, так что большинство диспетчеров используют отдельные области для выделения блоков, размер которых кратен некоторому числу, скажем, 16 байтам. Если вы запрашиваете блок размером 16 байтов, все отлично; но если вы запросите 17 байтов, то память будет выделена из области для 32-байтовых блоков, и 15 байтов памяти пропадут впустую. Это источник дополнительных расходов памяти, но об этом мы поговорим чуть позже. Очевидный вопрос звучит следующим образом: кто выбирает используемую стратегию управления памятью? Выбор стратегии 2. В чем состоит отличие различных уровней управления памятью в контексте стандартной библиотеки С++ и типичных средах, в которых используются реализации этой библиотеки? Что можно сказать об их взаимоотношениях, как они взаимодействуют друг с друго.м и как между ними распределяются обязанности? Имеется ряд возможных уровней управления памятью, каждый из которых может скрывать предыдущий уровень. Кроме того, за дополнительной информацией о распределении памяти можно порекомендовать обратиться, например, к разделу 2.S книги Д. Клут Искусство программирования, том 1. Основные алгоритмы, 3-е изд. - М.: Издательский дом "Вильяме", 2000. - Прим. ред. • Ядро операционной системы предоставляет базовые услуги по распределению памяти. Эта базовая стратегия распределения памяти и се свойства могут изменяться от одной операционной системы к другой, и на этот уровень в наибольшей степени влияет используемое аппаратное обеспечение. • Библиотека времени выполнения компилятора, используемая по умолчанию, содержит собственные средства работы с памятью, такие как оператор new в С++ или функция та!loc в С, которые работают с использованием собственных служб распределения памяти. Эти службы, предоставляемые компилятором, могут представлять собой всего лишь небольшую обертку вокруг соответствующих служб операционной системы и наследовать их свойства. Возможно и другое решение, когда система управления памятью, предоставляемая компилятором, перекрывает стратегию операционной системы, получая от нее блоки большого размера, которые затем перераспределяет в соответствии с собственной стратегией. • Стандартные контейнеры и распределители используют сервисы, предоставляемые компилятором и, в свою очередь, могут перекрывать их путем реализации собственных стратегий и оптимизаций. • И наконец, пользовательские контейнеры и/или пользовательские распределители могут использовать любой из сервисов более низкого уровня (например, они могут обращаться непосредственно к сервисам операционной системы, если для данной программы не имеет значения переносимость) и работать независимо от них, т.е. так, как того хочет автор. Все эти уровни показаны на рис. 20.1. Пользовательские контейнеры и/или распределители Стандартные контейнеры и распределители Оператор new и функция та 11 ос Ядро операционной системы Рис. 20.1. Основные уровни управления памятью. Обычно каждый уровень реализуется посредством более низкого уровня(ей) Таким образом, распределение памяти осуществляется различными способами и может варьироваться от операционной системы к операционной системе, от компилятора к компилятору в пределах одной операционной системы, от контейнера к контейнеру - и даже от объекта к объекту. Так, в случае использования объекта vec-tor<int> применяется стратегия, реализованная в а! 1 ocator<int>, а в случае объекта vector<int, MyAllocatoo может использоваться совершенно иная стратегия выделения памяти. > Рекомендация Никогда не мешает знать, кто и за что отвечает. Потратьте немного времени, чтобы точно выяснить, какие стратегии используются на каждом уровне в используемой вами среде программирования. Резюме Управление памятью в современных операционных системах может быть очень сложным, но это - только один из уровней управления памятью, имеющий значение для программ на С+ + . Стандартная библиотека предоставляет несколько других уровней, в первую очередь при помощи собственных примитивов для выделения и освобождения памяти, посредством стандартных контейнеров и распределителей, а также контейнеров и распределителей памяти, которые вы можете написать самостоятельно. Но когда вы запрашиваете память, знаете ли вы о том, что вы получите на самом деле, и во что это вам обойдется? Сколько памяти требуется стандартным контейнерам - в теории, и на практике? Именно об этом мы и поговорим в следующей задаче. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 [ 41 ] 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |