Папка WinSxS в каталоге C:\Windows - это самое недопонятое место во всей операционной системе. Администратор открывает свойства, видит 18, 22, а то и 30 гигабайт, и у него чешутся руки взять и удалить половину. Особенно обидно, когда тот же самый файл вроде бы лежит и в System32, и в WinSxS, и складывается впечатление, что Windows просто жадно дублирует одни и те же библиотеки. На самом деле хранилище компонентов устроено тоньше, чем выглядит, и почти весь его видимый размер - это либо оптическая иллюзия из-за жёстких ссылок, либо осознанная плата за возможность откатить обновления и восстановить систему без установочного диска. В этой статье разбираем устройство WinSxS глазами deployment-инженера: что там реально лежит, почему проводник врёт про размер, как растут кумулятивные обновления и как чистить хранилище честными средствами, не ломая сервисный стек.
Side-by-side сборки, зачем Windows держит несколько версий одной DLL
Название WinSxS расшифровывается как Windows Side-by-Side. Идея появилась как ответ на старую боль эпохи Windows 9x и раннего XP, известную как DLL Hell: установщик одной программы перезаписывал общую библиотеку в System32 своей версией, и после этого падали три другие программы, собранные под другую ревизию той же DLL. Одна точка хранения на всех означала, что любой инсталлятор мог сломать полсистемы.
С выходом Windows XP и особенно с Vista Microsoft перешла на модель side-by-side сборок. Каждая сборка (assembly) - это версионированный набор файлов с манифестом, где жёстко прописаны имя, версия, архитектура, язык и хэш открытого ключа издателя. В WinSxS имена каталогов выглядят примерно так: amd64_microsoft-windows-something_31bf3856ad364e35_10.0.22621.3007_none_длинныйхэш. Из самого имени видно, для какой архитектуры собран компонент, какая у него версия и чья это подпись.
Смысл в том, что несколько версий одной и той же библиотеки могут мирно сосуществовать плечом к плечу. Приложение при загрузке указывает, какую версию оно хочет, и загрузчик подсовывает ему именно её - через манифесты и политики перенаправления. Старое приложение, собранное под старую DLL, продолжает получать старую DLL, а свежее - свежую. Никто никого не перезаписывает. Расплата очевидна: дисковое пространство. Три актуальных версии одной библиотеки - это три копии в хранилище. Умножьте на тысячи компонентов системы, и объём набегает сам собой.
Жёсткие ссылки, почему файл «дважды» на самом деле лежит один раз
Теперь самое интересное про размер. Откройте какую-нибудь системную DLL в C:\Windows\System32 и найдите её же в глубинах WinSxS. Похоже на дубликат? На самом деле дубликата нет. Подавляющее большинство файлов в System32, SysWOW64 и других системных каталогах - это жёсткие ссылки (hard links) на содержимое, физически лежащее в WinSxS.
Жёсткая ссылка - это второе имя для того же самого набора данных на томе NTFS. Контент один, а имён у него может быть сколько угодно. Байты на диске заняты один раз, но и dir, и проводник считают каждую ссылку как отдельный полноценный файл. В результате происходит двойная иллюзия. Если сложить размеры WinSxS и System32 по показаниям проводника, получится, что система занимает заметно больше, чем реально съедено на диске. А внутри самой WinSxS проводник тоже врёт в обратную сторону: он показывает файлы, на которые ссылаются из System32, как будто они целиком принадлежат хранилищу.
Именно поэтому скрипты «посчитать настоящий размер WinSxS» дают разные цифры в зависимости от того, учитывают ли они жёсткие ссылки. Единственный честный способ узнать фактический расход - спросить сам сервисный стек: команда Dism.exe /Online /Cleanup-Image /AnalyzeComponentStore выдаёт отчёт, где отдельно указан фактический размер хранилища с учётом ссылок и отдельно - то, что реально можно освободить. Все остальные методы, включая свойства папки в проводнике, грешат систематической ошибкой.
Из этого устройства следует практический вывод. Удаление файла из System32 по сути не освобождает место, потому что данные продолжают жить в WinSxS. А удаление из WinSxS не «чистит дубль», оно выдёргивает единственную копию контента, оставляя висячие ссылки. После такой ручной зачистки система обычно выглядит живой ровно до первого обновления или первого sfc /scannow.
Почему кумулятивные обновления раздувают хранилище
Второй двигатель роста - сервисная модель Windows. Каждое кумулятивное обновление не просто заменяет файлы, оно оставляет в хранилище предыдущее состояние компонентов. Зачем? Чтобы обновление можно было удалить и откатиться, и чтобы проверка целостности (SFC, DISM /RestoreHealth) имела эталон, с которым можно сравнить повреждённый файл и из которого его можно восстановить.
Механика такая. При установке обновления CBS (Component Based Servicing) кладёт новые версии файлов в WinSxS, а старые не стирает, а помечает как вытесненные (superseded). Пока высидевшее обновление можно удалить через «Установленные обновления», старые версии обязаны лежать на месте. Через год ежемесячных кумулятивных обновлений хранилище накапливает заметный слой таких вытесненных компонентов - иногда 3-6 гигабайт, а на долгоживущих серверах и больше.
Отдельная история - базовые каталоги резервного копирования и промежуточные состояния при установке крупных обновлений и апгрейдов версий. После feature update рядом появляется Windows.old, но и само хранилище прибавляет, потому что новая сборка ОС тащит новые версии практически всех компонентов, а старые держатся ради отката. Хорошая новость в том, что эта болезненность роста ограничена: Windows сама умеет убирать вытесненные версии, просто делает это по своему расписанию, а не в момент, когда администратору захотелось.
Честная очистка, StartComponentCleanup и ResetBase
Единственный корректный способ похудеть хранилищу - попросить об этом сам сервисный стек. Всё остальное - лотерея с непредсказуемыми последствиями.
- Узнать реальное состояние: Dism.exe /Online /Cleanup-Image /AnalyzeComponentStore - покажет фактический размер, число вытесненных компонентов и рекомендацию, нужна ли очистка.
- Мягкая очистка: Dism.exe /Online /Cleanup-Image /StartComponentCleanup - удаляет вытесненные версии компонентов, оставляя возможность удалить установленные обновления в составе текущей базы.
- Жёсткая очистка: Dism.exe /Online /Cleanup-Image /StartComponentCleanup /ResetBase - дополнительно схлопывает базу отката: после неё все уже установленные обновления удалить нельзя вообще, пункт «удалить обновление» исчезает из списка.
Про ResetBase нужно понимать главное: это необратимая операция. Она фиксирует текущее состояние системы как новую базовую точку. Если через неделю выяснится, что последнее кумулятивное обновление ломает бухгалтерскую программу, откатить его штатно уже не получится - придётся разворачивать образ или переустанавливать систему. Поэтому ResetBase имеет смысл запускать только на стабильной системе, после периода обкатки свежих обновлений, а не сразу после вторника патчей. На эталонных образах для развёртывания, наоборот, ResetBase - стандартный финальный шаг перед захватом install.wim: образ выходит заметно компактнее.
Есть ещё параметр /SPSuperseded для удаления файлов установленного сервис-пака, но в эпоху Windows 10 и 11, где сервис-паков как класса больше нет, он почти не используется.
Планировочная самоочистка и почему лучше ей доверять
Многие не знают, что Windows умеет прибираться сама. В планировщике заданий есть задача StartComponentCleanup (Microsoft\Windows\Servicing), которая запускается при простое системы и удаляет вытесненные компоненты, которым больше примерно 30 дней. Выдержка в месяц сделана неспроста: это гарантия, что прошёл как минимум один цикл кумулятивных обновлений и шанс понадобиться старым версиям ради отката сведён к минимуму.
Практическое следствие: на здоровой системе, которая регулярно включается и простаивает хоть немного, хранилище стабилизируется около некоторого рабочего объёма и перестаёт расти линейно. Паника «WinSxS съела весь диск» чаще всего означает либо молодую систему после серии апгрейдов, либо машину, которую вечно гоняют под нагрузкой без простоя, либо фантазии проводника про жёсткие ссылки. Прежде чем воевать с папкой, стоит один раз запустить AnalyzeComponentStore и посмотреть, что скажет сам стек. Если в отчёте cleanup recommended - no, то и повода для суеты нет.
Очистка диска cleanmgr тоже умеет дёргать сервисный стек через пункт «Очистка обновлений Windows», и это тот же безопасный механизм StartComponentCleanup, просто с графической мордой.
Чем WinSxS не является, Installer, $NtUninstall и Features on Demand
Путаница усугубляется тем, что у Windows есть ещё несколько «разрастающихся» мест, и их часто смешивают в одну кучу.
Каталог C:\Windows\Installer - это кэш MSI-пакетов и патчей установленных программ. К хранилищу компонентов он отношения не имеет, но чистить его вручную так же нельзя: без кэшированного MSI программа не может восстановиться, обновиться или удалиться, и переустановка превращается в археологию. Там правило то же, что и в WinSxS: только штатные средства.
Каталоги $NtUninstallKB... в корне Windows - это наследие эпохи XP и 2003: отдельные папки отката для каждого хотфикса. В старые времена их удаляли руками относительно безболезненно, теряя только возможность снести конкретный хотфикс. Начиная с Vista вся эта функциональность переехала внутрь WinSxS и CBS, поэтому аналогия «WinSxS - это просто новые $NtUninstall» неверна: хранилище - это не архив откатов, а живой репозиторий всех компонентов системы, из которого она собирается и лечится прямо сейчас.
Отдельная разумная диета для хранилища - удаление ненужных компонентов через Features on Demand и необязательные компоненты. Команды вида Dism.exe /Online /Disable-Feature /FeatureName:Имя /Remove убирают полезную нагрузку компонента из хранилища совсем, а не просто отключают его. То же делают Remove-WindowsCapability и Remove-WindowsOptionalFeature в PowerShell. Если на сервере никогда не понадобится, скажем, старый клиент SMB1 или куча языковых пакетов, их вынесение из образа даёт честную экономию без вреда для сервисного стека - стек сам обновляет манифесты и знает, что компонента больше нет.
Пределы восстановления и почему rd /s - это приговор
Теперь о главном запрете. Кажется, что раз файл лежит и в System32, и в WinSxS, то удаление копии из WinSxS ничего не сломает. Это в корне неверно, потому что «копия» в WinSxS - это и есть единственный контент. После ручного удаления части хранилища сценарий развивается предсказуемо: сначала всё работает, потом приходит очередное кумулятивное обновление, CBS пытается прочитать нужную версию компонента из хранилища, не находит её и падает с ошибкой в духе 0x80073712 или STATUS_SXS_COMPONENT_STORE_CORRUPT. Проверка sfc /scannow докладывает, что нашла повреждения, но исправить не может, потому что источник восстановления - тот самый WinSxS - уже искалечен. DISM /RestoreHealth тоже спотыкается: локального источника больше нет, приходится подсовывать /Source с чистого дистрибутива той же сборки, и то не факт, что удастся залатать все дыры.
А удалённые через /Remove компоненты Features on Demand восстановить с локального диска уже нельзя в принципе - их придётся качать через Windows Update или брать с дистрибутива Features on Demand. Это осмысленная цена за честное уменьшение, и стек о ней предупреждает. Цена за нечестное уменьшение через rd /s /q - непредсказуемая.
Итог прост и неперевариваем для перфекционистов: WinSxS большой, потому что Windows - это система с поддержкой версионирования компонентов, отката обновлений и самовосстановления, а всё это стоит дискового пространства. Проводник врёт про реальный размер из-за жёстких ссылок. Чистить хранилище можно и нужно, но только через Dism.exe /Online /Cleanup-Image /StartComponentCleanup, /ResetBase после обкатки обновлений, удаление лишних компонентов через Features on Demand и штатную задачу планировщика. А руки, тянущиеся к Shift+Delete на папке WinSxS, лучше сразу занять чем-нибудь полезным - например, запуском AnalyzeComponentStore, который скажет правду о том, сколько места реально можно освободить.