This is a good list. Other reasons are that the JIT has more information about the program. The Java-to-bytecode compiler never sees the whole application, and it knows nothing about which program paths are actually executed in practice. The JIT compiler, in contrast, knows about the loaded class hierarchy, and it has access to type and branch profile information. This enables the JIT to do aggressive inlining of method calls and pruning of branches that are never executed. These are probably the most important transformations that lead to the discovery of opportunities for these mask optimizations. The source code that javac sees will contain very few relevant cases.