A Tour of SQL Server Page Types: Data, GAM, SGAM, PFS, IAM

A woman engineer holding a map before a tall stack of uniform 8 KB pages, most plain data pages with a few special map, free-space, and allocation tracking pages marked, illustrating the different page types in a SQL Server data file.

Most of this storage series has been spent inside data pages, pulling rows apart byte by byte. But a data file is not only data pages. Scattered through it, at fixed and predictable spots, are pages whose entire job is to track other pages: which extents are allocated, which have free space, how full each page is, and which pages belong to which table. This post takes a tour of those allocation and tracking pages, reads a few of them on a real database, and explains what the engine uses each one for.

This continues the byte-level storage series alongside the NULL bitmap, row-overflow and LOB, and where each column lives in a record. If DBCC PAGE is new to you, this older post covers the mechanics. Every value below is from a SQL Server 2019 instance, and the script is included so you can reproduce it.

The cast

Every page is 8 KB and starts with a 96-byte header, and that header’s m_type says what the page is.[1] An extent is eight contiguous pages, and the allocation pages exist to manage pages and extents efficiently rather than to hold your rows:

Page type m_type What it tracks
Data 1 Your rows
Index 2 B-tree index rows
GAM 8 Which extents are allocated (uniform extents)
SGAM 9 Mixed extents that still have free pages
PFS 11 Per-page allocation and how full each page is
IAM 10 Which extents belong to one allocation unit

The GAM, SGAM, and PFS pages sit at fixed offsets in every data file: PFS is page 1, GAM is page 2, SGAM is page 3, and they repeat at regular intervals as the file grows. That fixed placement is why the engine can find them without looking anything up.

Which pages did one table get?

Build a small table and ask sys.dm_db_database_page_allocations what it allocated:

Two hundred thousand-byte rows filled 36 data pages, and the table also owns IAM pages. The IAM is how the engine knows which extents in the file hold this table’s data, since a heap’s pages are not chained in order. Every allocation unit (the in-row, row-overflow, and LOB units from the previous post) gets its own IAM chain.

Reading the PFS page

Page 1 of the file is the PFS, Page Free Space. It stores one byte per page describing that page’s allocation state and how full it is. Dumping it (DBCC PAGE (db, 1, 1, 3)) produces a readable inventory:

Each line is a page or run of pages, its allocation state, and a fullness bucket: 0, 50, 80, 95, or 100_PCT_FULL. That fullness figure is what SQL Server consults when it needs somewhere to put a new row in a heap or a LOB, so it does not have to scan pages hunting for space. The Mixed Ext and IAM Page flags mark pages that live in mixed extents and the IAM pages themselves. The PFS page tracks about 8,000 pages, then another PFS page picks up where it left off.

Reading GAM and SGAM

The GAM (page 2) and SGAM (page 3) manage allocation at the extent level. The GAM has one bit per extent: set means the extent is free, clear means it is allocated as a uniform extent. The SGAM tracks mixed extents, the extents whose eight pages can belong to different objects, that still have a free page to hand out. Between them the engine can find a free extent or a free page in a mixed extent by reading a couple of bitmap pages rather than walking the file. Dumping GAM or SGAM with DBCC PAGE prints the allocation state of each extent as a range, for example (1:0)-(1:3) = ALLOCATED.

What this means in practice

  • Allocation is bitmap-driven, not a scan. When you insert a row and SQL Server needs a page, it does not search; it reads the GAM, SGAM, and PFS to locate free space directly. This is why allocation stays fast as a file grows.
  • These pages can be a contention point. Because GAM, SGAM, and especially PFS pages are fixed, hot spots on them can serialize allocation under heavy concurrent inserts, the classic tempdb allocation contention, addressed by multiple data files and, on modern versions, by default optimizations.
  • The IAM is why a heap can be scanned at all. With no clustered index chaining pages together, the IAM chain is the map of which extents hold the table. A DBCC PAGE tour makes it obvious that “the table” is really a set of extents an IAM points at.
  • You rarely touch these directly, but they explain a lot. Space that will not release, allocation contention, and the mechanics of how DBCC CHECKDB and allocation checks work all trace back to these pages. Knowing they exist turns several mysteries into plumbing.

The row-level posts in this series show where your data sits; the allocation pages show how SQL Server keeps track of all of it without ever scanning the file to find room. It is bitmaps and fixed offsets all the way down.

Which of these pages have you run into in the wild, PFS contention in tempdb, or an IAM chain in a DBCC message? I would like to hear it. Find me on Bluesky or LinkedIn.

References

  1. Page and Extent Architecture Guide – Microsoft Learn. The 8-KB page and 96-byte header, the page types (data, index, GAM, SGAM, PFS, IAM, and others), extents as eight contiguous pages, and the roles of the allocation and tracking pages.