Caching, the last lesson, is about not repeating work. This lesson is about finding out what's actually slow before changing anything, since fixing the wrong layer wastes time and can make the real bottleneck harder to see.
Learning Objectives
After completing this lesson, you'll be able to:
- Identify why the database layer is usually the first place to look.
- Apply code-level habits (scoping, native functions, loop hygiene) that avoid unnecessary overhead.
- Use real, documented JVM tuning flags instead of generic "increase the heap" advice.
- Know which performance advice is ColdFusion-specific versus general web server configuration.
How Performance Tuning Fits Together
Measure First
find the actual bottleneck
Fix the Database Layer
almost always the biggest win
Tune the JVM / Server
heap, GC, connection settings
Measure Again
confirm it actually helped
The Database Layer Is Usually the Bottleneck
Adobe's own optimization guide states plainly that stored procedures are faster than cfquery tags, since database systems are built to execute them efficiently. Beyond that, the query-caching tools covered in the last lesson, cachedwithin/cachedafter and cfcache, apply directly here: the fastest query is one that doesn't run a second time.
<cfloop query="getUsers">
<cfquery name="getOrders" datasource="myDSN">
SELECT * FROM orders WHERE userID = <cfqueryparam value="#getUsers.id#" cfsqltype="cf_sql_integer">
</cfquery>
</cfloop>1,000 users means 1,000 separate queries. A single query with a JOIN, or one query using an IN clause (list="true" on cfqueryparam, covered in the SQL Injection Prevention lesson), replaces all of them.
Code-Level Habits
- Scope variables explicitly (arguments.x, local.x, session.x). An unscoped variable makes CFML search through several scopes in order to find it, every single time it's referenced.
- Prefer native member functions (array.map(), struct.each(), and similar) over a hand-written loop doing the same thing, they run as optimized built-ins rather than interpreted CFML.
- Keep expensive operations (creating a UUID, instantiating a component, running a query) out of loop bodies, each pass repeats the cost instead of paying it once.
Real JVM and Server Tuning
Generic advice to "increase the heap size" isn't that actionable. Adobe's own troubleshooting guide gives specific flags to set in jvm.config.
| Setting | Purpose |
|---|---|
| -Xmx (max heap size) | Raise it above the 1GB default based on actual application memory usage |
| -XX:MaxMetaspaceSize | Limits native memory used for class metadata |
| -XX:+UseG1GC | Recommended garbage collector for heaps of 4GB or more, in place of the default parallel GC |
| -XX:-UseCodeCacheFlushing | Prevents CPU spikes caused by the JVM repeatedly flushing its compiled-code cache |
| -XX:-TieredCompilation | On Java 1.8 specifically, disables tiered compilation where it's causing issues |
| -XX:+HeapDumpOnOutOfMemoryError | Writes a heap dump automatically on an OutOfMemoryError, paired with -XX:HeapDumpPath=<path> |
A heap dump is analyzed with the Eclipse Memory Analyzer Tool (MAT). For a hung or deadlocked request specifically, CF11 Update 12+ includes takethreaddump.cfm for capturing a thread dump without restarting the server.
A Real Detail: Database Connections Close Slowly on Purpose
ColdFusion closes a maximum of 5 timed-out datasource connections at each interval, this isn't configurable. Adobe's troubleshooting guide recommends setting timeout to 5 and interval to 1 on the datasource's connection settings so stale connections actually get cleared promptly instead of piling up.
Reduce and Cache External Calls
An outbound cfhttp call (covered in Module 14) adds the remote server's latency directly onto your own page's response time. If the same request is made on every page load and the data doesn't need to be real-time, cache the response the same way you'd cache a query, with cachePut() and a reasonable timespan.
Move Heavy Work Out of the Request
Sending a large batch of emails, generating a report, or processing an image doesn't need to happen while a user is waiting on a response. cfschedule (Module 14) or a queued background task keeps the actual HTTP request fast, with the heavy work happening separately.
A Real Distinction: What ColdFusion Controls vs. General Web Performance
Compressing responses (gzip/Brotli), minifying CSS/JS, using a CDN for static assets, and lazy-loading images are all genuinely useful, but none of them are ColdFusion settings. They're typically configured at the web server or reverse proxy layer (IIS, Apache, Nginx), in front of ColdFusion rather than inside it. Worth knowing which layer actually owns a given optimization before looking for it in the ColdFusion Administrator.
Common Beginner Mistakes
Tuning the JVM before confirming that's actually the bottleneck
The database layer is the more common bottleneck. Measure first (slow query logs, actual CPU/memory usage) rather than assuming JVM flags are the fix.
Running a query inside a loop over another query's results
This is the classic N+1 pattern: one query per row of an outer result set. A single query with a JOIN or an IN clause almost always replaces the whole loop.
Assuming gzip compression or CDN usage is a ColdFusion Administrator setting
These live at the web server/reverse proxy layer, not in ColdFusion itself. Looking for them in the wrong layer wastes time.
Increasing heap size as the default fix for any memory issue
A larger heap without the matching garbage collector (like G1GC for 4GB+ heaps) can trade OutOfMemory errors for long GC pauses instead. The two settings go together.
Best Practices
- Measure first, identify the actual bottleneck, optimize, then measure again, in that order.
- Look at the database layer first: stored procedures, query caching, and eliminating queries-in-loops before anything else.
- Use the specific JVM flags that match the actual symptom (heap size for OOM, G1GC for large heaps, thread dumps for hangs), not generic tuning.
- Know which layer (ColdFusion vs. web server) actually owns a given optimization before looking for it in the wrong place.
Interview Questions
Why is a query running inside a loop considered a serious performance problem?
It turns what should be one database round-trip into one per iteration (the N+1 pattern). A single query with a JOIN or an IN clause replaces all of them with one round-trip.
Why does unscoped variable access cost more in CFML than a scoped reference?
CFML has to search through multiple scopes in a defined order to resolve an unscoped variable. Scoping it explicitly skips that search entirely.
When would you reach for -XX:+UseG1GC specifically?
On a JVM heap of 4GB or larger, where it's recommended over the default parallel garbage collector.
Summary
In this lesson, you looked at the database layer first (stored procedures, avoiding queries in loops), applied code-level habits like explicit scoping and native functions, used real JVM tuning flags for heap size and garbage collection, and drew a clear line between what ColdFusion itself controls and what belongs at the web server layer.
What's Next?
The next lesson, Server Monitoring, closes out Module 16: knowing what to actually watch so you catch a performance problem before a user reports it.