Может ли сборщик мусора переместить структуру во время вызова?

Я реализую векторный тип на основе стека следующим образом:

    [StructLayout(LayoutKind.Sequential)]
    public struct VectorI16x8 {
        public short s0, s1, s2, s3, s4, s5, s6, s7;
        public short this[int i] {
            get => UnsafeValues[i];
            set => UnsafeValues[i] = value;
        }
        public int Length => 8;
        private Span<short> UnsafeValues => MemoryMarshal.CreateSpan(ref s0, Length);
    }

Совершенно очевидно, что если бы я сделал UnsafeValues общедоступным, значения этой структуры можно было бы перемещать в памяти (даже без сборщика мусора), что приводило бы к висячей ссылке внутри Span или к тому, что Span указывало бы на неправильный экземпляр. .

Однако менее ясно, безопасно ли использование такого диапазона в моей реализации структуры. Мой вопрос: могу ли я предположить, что моя структура this не будет перемещаться во время выполнения моего индексатора (это означает, что диапазон, созданный UnsafeValues, останется действительным), или мне придется использовать блок fixed, чтобы сделать это безопасным?

В примечаниях к методу упоминается следующее: Этот метод может быть полезен, если часть управляемого объекта представляет собой фиксированный массив.

Sinatr 21.08.2024 16:36

по касательной: возможно, вы захотите взглянуть на «встроенные массивы» — рассмотрите: [InlineArray(8)] public struct VectorI16x8 { private short _element0; }Sharplab.io/…

Marc Gravell 21.08.2024 16:37

@MarcGravell Я знаю о встроенных массивах, но не могу использовать в Unity ничего более нового, чем .NET Standard 2.1. Кроме того, я хочу представить все значения как именованные поля, а также сделать их индексируемыми.

zstewart 21.08.2024 16:45

@Sinatr Я считаю, что это относится к структуре, представляющей массив фиксированного размера, что и есть. Но это не говорит мне о требованиях к закреплению для использования структуры в целом, и есть довольно большое предупреждающее сообщение о том, что время жизни возвращаемого диапазона не будет должным образом отслеживаться даже в языках, поддерживающих диапазоны.

zstewart 21.08.2024 16:49

нет никаких требований к закреплению — в этом весь смысл использования управляемых указателей; конечно, это предполагает, что Unity правильно понимает всю эту логику ;p

Marc Gravell 21.08.2024 16:53
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для этого вам не требуется блок fixed; короткая версия заключается в том, что для обработки этого сценария требуется сборщик мусора. Промежутки принципиально ничем не отличаются от любого другого «внутреннего указателя» — и та же проблема может быть инициирована каким-либо методом в вашей структуре, вызывающим DoSomething(ref s0), DoSomethingElse(in s1) и т. д. Это одна из причин, почему управляемые указатели (ref, Span<T> и т. д.) могут быть только хранятся в стеке, а не как поля типов (кроме ref struct) - чтобы сборщик мусора знал, где их проверять (ему в любом случае нужно пройти по стековым фреймам, проверить маркеры fixed и т. д. - которые являются просто специальными токенами для локальных переменных) ). Справится ли сборщик мусора с этим, решив не перемещать эти регионы (как если бы они были fixed) или исправить ссылки (во время паузы), является деталью реализации сборщика мусора.

(небольшое примечание: в зависимости от среды выполнения существует диапазон из 3 или 2 полей; они проектируются по-разному в зависимости от возможностей среды выполнения для обработки этого сценария, но в любом случае короткая версия - это «волшебство происходит» и это работает без паники)

Рад, что вы ответили вместо того, чтобы кто-то дал случайную чушь «типы значений находятся в стеке и не обрабатываются сборщиком мусора», которая игнорирует распространенные сценарии, такие как структуры как член класса.

Joel Coehoorn 21.08.2024 16:30

Однако это делает то, что вы не можете сделать с обычной ссылкой: структурам не разрешено возвращать ссылки на себя или свои поля, но это фактически то, что делает UnsafeValues, поэтому мне интересно, имеет ли это значение? Кажется, это определенно было бы безопасно, если бы я встроил вызов MemoryMarshal.

zstewart 21.08.2024 16:36

@zstewart да, на этом основании могут быть некоторые проблемы - и действительно, ref в CreateSpan объявлен scoped, так что можно надеяться, что компилятор здесь станет суетливым; однако, если там есть проблема: она не относится к fixed vs not - и, скорее всего, связана с объявлением экранирования ссылки и повреждением стекового пространства из-за одновременного использования одного и того же фрагмента для несовместимых целей. Однако я не могу добиться сбоя локально.

Marc Gravell 21.08.2024 16:51

Далее: учтите, что поля могут быть public, и в этом случае вызывающий абонент может получить ref к полям. Разные оттенки одной и той же проблемы.

Marc Gravell 21.08.2024 16:55

Мой вывод таков: хотя компилятор не помешает мне создать висячую ссылку с помощью MemoryMarshal.CreateSpan, пока ссылка все еще находится в живой памяти, GC не сломает ее.

zstewart 21.08.2024 19:25

Другие вопросы по теме