Salesforce Governor Limits Explained (with a Cheat Sheet)
Governor limits are per-transaction resource caps Salesforce enforces to protect its shared, multitenant infrastructure. The ones you'll hit most: 100 SOQL queries, 150 DML statements, 10 seconds of CPU time, and 6 MB of heap per synchronous transaction. Exceeding any of them throws an uncatchable exception and rolls the transaction back.
The cheat sheet (per transaction)
| Limit | Synchronous | Asynchronous |
|---|---|---|
| SOQL queries issued | 100 | 200 |
| Records retrieved by SOQL | 50,000 | 50,000 |
| Records retrieved by Database.getQueryLocator | 10,000 | 10,000 |
| SOSL queries issued | 20 | 20 |
| DML statements issued | 150 | 150 |
| Records processed by DML | 10,000 | 10,000 |
| CPU time | 10,000 ms | 60,000 ms |
| Heap size | 6 MB | 12 MB |
| HTTP callouts | 100 | 100 |
| Cumulative callout timeout | 120 s | 120 s |
| Future method invocations | 50 | 0 in batch/future context |
| Queueable jobs enqueued | 50 | 1 (chaining) |
| Maximum transaction execution time | 10 minutes | |
Values are the standard published limits — always confirm edge cases against the official Salesforce governor limits reference, since Salesforce occasionally revises them.
Sync vs async: which budget are you on?
A transaction is synchronous when it runs in response to a user action or API call (record save, Lightning component call, REST request). It's asynchronous — with the bigger CPU/heap budget — when it runs as batch Apex, a future method, a queueable, or scheduled Apex. This is why "move it async" is a standard fix for CPU time limit errors: the same code gets six times the CPU budget and runs outside the user's save.
How limits appear in a debug log
Near the end of every log, the LIMIT_USAGE_FOR_NS event reports consumption per namespace:
LIMIT_USAGE_FOR_NS|(default)|
Number of SOQL queries: 96 out of 100 ******* CLOSE TO LIMIT
Number of query rows: 12043 out of 50000
Number of DML statements: 12 out of 150
Maximum CPU time: 4210 out of 10000
Maximum heap size: 1873921 out of 6000000
Key detail: certified managed packages get their own bucket for most counted limits (their own 100 queries, etc.), which is why the report is namespaced — but CPU time and heap are shared across every namespace in the transaction. A greedy package spends your CPU.
ForceLens's Limits tab renders this section as a visual dashboard with per-limit progress bars, so a 96/100 SOQL situation is impossible to miss. Combined with the SOQL tab, you can jump from "too many queries" straight to which query fired 96 times.
Monitoring limits from code
System.debug('SOQL: ' + Limits.getQueries() + ' / ' + Limits.getLimitQueries());
System.debug('CPU: ' + Limits.getCpuTime() + ' / ' + Limits.getLimitCpuTime());
System.debug('Heap: ' + Limits.getHeapSize() + ' / ' + Limits.getLimitHeapSize());
The System.Limits class reads live consumption mid-transaction — useful for defensive branching in loops that might approach a cap, and for instrumenting suspect code paths with USER_DEBUG checkpoints you can then trace in the log.
Staying out of trouble
- Never put SOQL or DML in a loop. Collect IDs, query once with
IN :ids, write once with a collection DML. This single habit prevents most SOQL 101 and DML 151 errors. - Watch query rows, not just query count. 50,000 retrieved rows is reachable by one unbounded query on a big object.
- Control trigger recursion — every re-entry re-spends every limit.
- Mind heap with large payloads. Building giant strings or deserializing big JSON eats the 6 MB fast; process in chunks or async.
- Check the log after big transactions, not just failing ones. "96 out of 100" today is an error next month when data grows.
Frequently asked questions
What are governor limits in Salesforce?
Per-transaction resource caps enforced because orgs share multitenant infrastructure: 100 SOQL queries, 150 DML statements, 10 s CPU, 6 MB heap (sync values), and more. Exceeding one throws an uncatchable limit exception and rolls back the transaction.
How do I see governor limit usage in a debug log?
Find LIMIT_USAGE_FOR_NS near the end of the log — it lists consumption against each maximum, per namespace. ForceLens renders the same data as a visual limits dashboard.
Do managed packages get their own limits?
Certified managed packages get separate buckets for most counted limits (SOQL, DML), but CPU time and heap are shared across all namespaces in the transaction.
How can I check limits from Apex code?
Use System.Limits methods — Limits.getQueries(), Limits.getCpuTime(), Limits.getHeapSize() and their getLimit* counterparts — to read live consumption at runtime.
See your limits, don't count them
Open any debug log in ForceLens and the Limits tab shows every governor limit as a progress bar — free, local, one click.