-Xmx doesn't control what you think it controls.
It only controls the JVM heap, not everything goes in the JVM heap, and the heap takes up way more native memory that what you specify for management and bookkeeping.
You can't control what you want to control, -Xmx only controls the Java Heap, it doesn't control consumption of native memory by the JVM, which is consumed completely differently based on implementation. The JVM uses up native memory in an entirely different way and it dependant on each JVM implementation and the OS it is running on.
From the following article Thanks for the Memory ( Understanding How the JVM uses Native Memory on Windows and Linux )
Maintaining the heap and garbage collector use native memory you can't control.
More native memory is required to maintain the state of the
  memory-management system maintaining the Java heap. Data structures
  must be allocated to track free storage and record progress when
  collecting garbage. The exact size and nature of these data structures
  varies with implementation, but many are proportional to the size of
  the heap.
and the JIT compiler uses native memory just like javac would
Bytecode compilation uses native memory (in the same way that a static
  compiler such as gcc requires memory to run), but both the input (the
  bytecode) and the output (the executable code) from the JIT must also
  be stored in native memory. Java applications that contain many
  JIT-compiled methods use more native memory than smaller applications.
and then you have the classloader(s) which use native memory
Java applications are composed of classes that define object structure
  and method logic. They also use classes from the Java runtime class
  libraries (such as java.lang.String) and may use third-party
  libraries. These classes need to be stored in memory for as long as
  they are being used. How classes are stored varies by implementation.
I won't even start quoting the section on Threads.
Plain and simple the JVM uses more memory than what is supplied in -Xms and -Xmx and the other command line parameters. 
The Classloaders, and applications can have more than one, eat up lots of memory that isn't documented easily. The JIT eats up memory, trading space for time, which is a good trade off most of the time. 
Some of the above links may refer to older Java versions; Java 8 handles garbage collection and memory allocation differently, but the general rules above apply.