It's similar to Goto Definition (F12). It's based on parsing according to the syntax definition (which only does line by line contextual parsing) so kinda like ctags. In practice it works better than the painfully slow "smart" and often non-existent* symbol navigation in IDEs (although QtCreator is very good in this regard).
Remarkably well. Because there's no macros or templates and not a lot of type inference in languages like Java and C#, it is very easy to look up the type name of an identifier.
Then, because in e.g. Java the fully qualified name of a type is forced to have a direct relationship to the directory structure¹, the IDE can just look up the .class file in which the type was defined. If the .class file doesn't exist or is outdated, the IDE recompiles but it only needs to do this once and then it can cache it. This .class file itself is a small file of JVM bytecode that allows very fast lookup of all its methods and attributes and whatnot.
This means that it's not a search at all - it's only a lookup (look up name of identifier, determine file location, look up definition), and project size hardly matters.
I often wonder whether they realized how easy they were making things for IDE builders when the Java team invented this stuff 20 years ago. If not, it's a pretty lucky hit and it works great. .NET, even with the benefit of Java-hindsight, didn't do this as well. Assemblies are nice but they make lookup and recompilation a bit more messy.
¹) nitpickers will complain about .jar files and the classpath, but you can look up once which types are in which directory or .jar, cache that, and you're done.
* for large projects