Из книжки "C# для профессионалов" т.1, стр. 46
... в отличие от более старых языков, где разработчик должен был указывать объекты, для которых требовалось использовать кучу, в C# этот выбор осуществляется компилятором в зависимости от типа данных объекта.
C# делит свои типы данных на две категории, основываясь на том, в каком из двух мест они хранятся. Переменные типов по значению хранят свои данные в стеке, а переменные типов по ссылке хранят свои данные в куче.
Место хранения типа данных указывает на то, как он будет вести себя в контексте оператора присваивания. Присваивание одной переменной по значению другой переменной по значению приводит к созданию двух разных копий одних и тех же данных в стеке. Присвоение одной переменной по ссылке другой переменной по ссылке приводит к появлению двух ссылок на подну и ту же позицию памяти.
и ниже
В C# основные типы данных, такие как bool и long, являются типами по значению.
Большинство сложных типов данных C#, включая классы, определяемые пользователем, являются типами по ссылке.
... необходимо помнить, что тип struct является в C# типом по значению.
на стр. 157 в подразделе "Переменные по ссылке и по значению: что происходит в памяти" читаем:
Необходимо помнить, что большая часть предлагаемого материала не документирована. Эта глава должна рассматриваться как упрощённое руководство, а не точное описание реализации схемы управления памятью.
т.е. в истинность нижеследующего никто не гарантирует - в лучших традициях Microsoft
для переменных по значению всё стандартно
Невозможно написать фрагмент кода на C#, в котором переменная B объявлена после A, но A выходит из области видимости первой.
т.е. стек
Отметим, что если компилятор встречает строку типа int I, J, то порядок вхождения в область видимости выглядит неопределённым.
далее
Нередко важно использовать некий метод для выделения памяти под некоторые данные и иметь доступ к ним ещё долго после выхода из метода. Такая возможность предоставляется, если память для хранения запрашивается с помощью оператора new - как в случае всех типов по ссылке.
Типы по ссылке включают в себя все классы, а также массивы. Например, строка:
int [] X = {3, 4, 5, 6, 7};
на самом деле является сокращённой записью для:
int [] X = new int [5] {3, 4, 5, 6, 7};
ну и для строк, соответственно, также.
Т.е. твоё копирование на деле есть лишь ссылка, а объект физически продолжает существовать один. Проверяй внимательно.
Кроме того, про сборщик мусора тут сказано, что он умный, и при очистке памяти выполняет её дефрагментацию - т.е. в какой-то момент программа может тормознуть из-за этого процесса.
У класса есть 3 стандартных деструктора: Close(), Dispose() и Finalize(). Где Close() - логический деструктор (логически завершает работу с объектом, но не удаляет его, т.е. объект можно продолжать использовать), Dispose() - полный ручной деструктор (после его выполнения объект удаляется, т.е. его нельзя больше использовать, но память не очищается), Finalize() - полный автоматический деструктор (его вызывает сборщик мусора, для ручного вызова он недоступен). Внутри Dispose следует вызвать System.GC.SuppressFinalize(), чтобы сборщик мусора не выполнял Finalize, когда будет перерабатывать кучу и физически удалять удалённый через Dispose объект.