The Pitfalls:

VisualVM

VisualVM Sampler and Profiler: To be able to drill down to the slow methods you need to take a snapshot before you stop the sampling/profiling (there is a [Snapshot] button above the Hot Spots list). This is not very intuitive and the interface doesn't communicate it.

VisualVM Profiler: Excludes Thread.sleep() & Object.wait() time (as opposed to the NetBeans profiler where you can choose to include/exclude them) => if you method is spending lot of time waiting for a lock, you won't discover it

While VisualVM was able to dynamically connect to my Tomcat, NetBeans wasn't able to do it and hasn't provided any notification about the failure. The only visible manifestation was that it wasn't showing and collecting any data. Solution: Use the "Direct" attach invocation, i.e. starting the target java application with the NetBeans profiling agentlib.

Tools Overview

VisualVM

Extremely useful tool, included in JDK since 6.0 (command line: jvisualvm) or on the VisualVM page.

Automatically discovers local Java processes and can connect to them (if they run Java 6+)

Monitoring - threads, heap, permgen, CPU, classes

Sampler - low-overhead profiling tool (takes thread snapshot at regular intervals and compares their stack traces to find out where most time is spent)

Plugins, such as MBeans, Tracers

Profiler - a simpler version of NetBeans profiler (comparison here); it can dynamically attach to running (Java 6+) processes and instrument classes on-the-fly. The not very visible checkbox Settings in the right-top corner can be used to set the classes to start profiling from (syntax: my.package.** to include subpackages or my.package.* or my.pkg.MyClass) and the packages not to / only to profile (syntax differs here: my.package.* to include subpackages or my.package. or my.pkg.).