Bloat is space the table holds but does not use: dead tuples not yet vacuumed, plus free space from tuples vacuumed long ago that inserts never refilled. Vacuum makes space reusable inside the file; it almost never shrinks the file itself, so a table that once bloated stays big until a rewrite. The steady-state bloat of a healthy table is roughly the dead rows that accumulate between two vacuum runs, which is why the trigger cadence, not vacuum speed, decides how fat a table runs.
DEMO — dead tuples over 60 d, toy table50M rows · 2M dead/day
day 015304560
autovacuum_vacuum_scale_factor0.20
default 0.2 · 0 decouples the trigger from table size
Measure it exactly with pgstattuple, or estimate with the check_postgres queries (~3% off on plain tables, far more on TOAST-heavy ones). Practitioners investigate above ~20-30% and rebuild above ~50%.