Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog Explainer

Does PostgreSQL COMMIT Release Memory? What Actually Gets Freed

Sean

Platform Writer

Jul 22, 2026
9 min read

A PostgreSQL COMMIT ends the transaction and releases transaction-lifetime resources, but it does not promise that the backend process RSS will immediately fall or that shared caches will be emptied.

Does PostgreSQL COMMIT Release Memory? What Actually Gets Freed

That distinction explains why a commit can solve transaction growth while operating-system tools still show a large postgres process. Database memory has several owners and lifetimes, and they must be measured separately.

Table of contents

What commit ends

COMMIT makes the transaction’s changes durable and ends its snapshot. Locks held until transaction end are released, temporary transaction contexts can be reset, and bookkeeping tied to that transaction becomes reusable. A very long transaction can retain row versions, locks, and internal state that block cleanup elsewhere, so committing in sensible batches matters even when memory is not the only concern.

The backend process usually stays alive for the connection. Its memory allocator may retain freed arenas for later requests rather than return every page to the operating system. PostgreSQL can therefore have freed memory for reuse while RSS remains flat. That is not automatically a leak.

Memory that does not disappear at commit

Shared buffers belong to the server, not one transaction, and remain useful as a cache. The operating system page cache also persists independently. Session-level objects and prepared statement state can live for the connection. Work memory is allocated by sort, hash, or other plan nodes and may involve several operations in one query; its peak behavior is not simply one work_mem allocation per session.

Temporary files may replace memory for large operations, but they introduce disk I/O. Connection pools keep backend processes alive, so their high-water marks can remain visible. A new connection appearing smaller than an old pooled connection is a clue about allocator reuse or session lifetime, not proof that COMMIT failed.

Why large transactions still cause pressure

Bulk loads, huge update batches, deferred constraints, and application-side result buffering can grow memory or hold cleanup back. One transaction also creates a larger failure and retry unit. If the operation permits it, commit bounded batches and make the process restartable. Batch size should be chosen from measured throughput, replication lag, lock duration, and recovery cost rather than an arbitrary row count.

BEGIN;
-- process a bounded batch
UPDATE jobs
SET processed_at = now()
WHERE id >= 100000 AND id < 110000;
COMMIT;

A commit boundary will not fix a client that loads an entire CSV or result set into its own memory. Measure the application and database separately.

How to diagnose retained memory

Start with PostgreSQL activity and query behavior, then correlate it with process and host metrics. Identify the backend PID, current query, transaction age, temp-file activity, connection-pool lifetime, and the moment memory grew. Use EXPLAIN with care on production data and inspect plans for multiple memory-intensive nodes.

SELECT pid, state, xact_start, query_start, wait_event_type, query
FROM pg_stat_activity
WHERE datname = current_database()
ORDER BY xact_start NULLS LAST;

If memory grows without bound under a repeatable workload across transaction and connection boundaries, investigate extensions, server version issues, and application behavior with a minimal reproduction. If it rises to a plateau and is reused on the next run, allocator caching is more likely.

Operational rules that prevent surprises

  • Keep transactions as short as correctness allows
  • Stream input and results instead of materializing everything
  • Set statement and idle-transaction timeouts deliberately
  • Size work_mem for concurrency, not for one ideal query
  • Monitor temp bytes, transaction age, RSS, and shared memory separately
  • Recycle pooled connections only for a measured reason, not as the first fix

The right question is not whether COMMIT returns every byte to the OS. It is whether memory remains bounded, reusable, and appropriate for the workload while transactions finish promptly and the host retains safe headroom.

How this fits the rest of the stack

Database memory competes with the rest of the deployment for real capacity. The RunxBuild hosting calculator lets you model the database and service together, and the RunxBuild dashboard keeps runtime and deploy logs available when a workload changes.

Useful related references:

FAQ

Does COMMIT free work_mem?

Memory used by completed query operations and transaction-lifetime contexts becomes reusable, but the backend allocator may not immediately return it to the operating system.

Why does the postgres process RSS stay high after commit?

The persistent backend can retain allocator arenas, while shared buffers and OS cache have longer lifetimes. Stable RSS alone is not proof of a leak.

Should bulk imports commit every row?

Usually no. Use bounded batches that balance throughput, locks, replication lag, retry cost, and durability requirements.

Does closing the database connection release more memory?

Ending a backend process returns its private memory to the OS, but routinely recycling connections can hide rather than solve an unbounded workload.

Can work_mem be allocated more than once per query?

Yes. Multiple sort or hash nodes and concurrent sessions can each allocate memory, so capacity planning must consider the full plan and concurrency.

#PostgreSQL#COMMIT#Memory#Transactions#Database