You could compile Objective C with a profiling or global analysing compiler that compiles method calls to different things depending on the class of the object if you wanted to, and you could compile Java with an approach that compiles to the same thing regardless of the object's class if you wanted do.
But message passing is always described as a programming model, not an invisible implementation detail.
Also implicit in message passing is a form of structural typing; objects that understand the same set of messages (called a protocol in Smalltalk-speak) are type-equivalent. A client class may delegate to any object that understands the messages the client sends to its delegate. One of the advantages this brings is all the crazy things you can do with #become: in Smalltalk. A perfectly valid way of responding to a message you haven't implemented is to construct an instance of a class that has an implementation for that message, and then become: that instance, swapping all references to yourself in system memory with a reference to the instance you created before passing the message onto the instance (your new self). There's no way to implement general #become: in C++ or Java because there's no guarantee the result will be sound type-wise.
The changes are subtle but they open up a world of dynamism that Java programmers don't have access to. Which is fine for Java programmers, who decided they don't want that kind of dynamism anyway, but there are programmers who work better shaping a live system rather than declaring type ontologies in advance. And Smalltalk is designed to work well with such programmers.
Are you telling me that if I turn on a compiler optimisation my message passing program suddenly becomes a method call system?
So the difference cannot be what it compiles to.
With message-send semantics as in Smalltalk or Objective-C, the compiler only knows the method's name, and calling a method foo compiles to the same thing irrespective of the object's class. It's the object's job to determine what foo means, and it will do the dispatch to determine which method to use, or if no method matches, call a missing-method handler.