ProGuard – Java class file Shrinker, Optimizer, Obfuscator and Preverifier
ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes. It optimizes bytecode and removes unused instructions. It renames the remaining classes, fields, and methods using short meaningless names. Finally, it preverifies the processed code for Java 6 or higher, or for Java Micro Edition.
Some uses of ProGuard are:
- Creating more compact code, for smaller code archives, faster transfer across networks, faster loading, and smaller memory footprints.
- Making programs and libraries harder to reverse-engineer.
- Listing dead code, so it can be removed from the source code.
- Retargeting and preverifying existing class files for Java 6 or higher, to take full advantage of their faster class loading.
ProGuard's main advantage compared to other Java obfuscators is probably its compact template-based configuration. A few intuitive command line options or a simple configuration file are usually sufficient. The user manual explains all available options and shows examples of this powerful configuration style.
ProGuard is fast. It only takes seconds to process programs and libraries of several megabytes. The results section presents actual figures for a number of applications.
ProGuard is a command-line tool with an optional graphical user interface. It also comes with plugins for Ant, for Gradle, and for the JME Wireless Toolkit.
What is shrinking?
Java source code (.java files) is typically compiled to bytecode (.class files). Bytecode is more compact than Java source code, but it may still contain a lot of unused code, especially if it includes program libraries. Shrinking programs such as ProGuard can analyze bytecode and remove unused classes, fields, and methods. The program remains functionally equivalent, including the information given in exception stack traces.
What is obfuscation?
By default, compiled bytecode still contains a lot of debugging information: source file names, line numbers, field names, method names, argument names, variable names, etc. This information makes it straightforward to decompile the bytecode and reverse-engineer entire programs. Sometimes, this is not desirable. Obfuscators such as ProGuard can remove the debugging information and replace all names by meaningless character sequences, making it much harder to reverse-engineer the code. It further compacts the code as a bonus. The program remains functionally equivalent, except for the class names, method names, and line numbers given in exception stack traces.
When loading class files, the class loader performs some sophisticated verification of the byte code. This analysis makes sure the code can't accidentally or intentionally break out of the sandbox of the virtual machine. Java Micro Edition and Java 6 introduced split verification. This means that the JME preverifier and the Java 6 compiler add preverification information to the class files (StackMap and StackMapTable attributes, respectively), in order to simplify the actual verification step for the class loader. Class files can then be loaded faster and in a more memory-efficient way. ProGuard can perform the preverification step too, for instance allowing to retarget older class files at Java 6.
Apart from removing unused classes, fields, and methods in the shrinking step, ProGuard can also perform optimizations at the bytecode level, inside and across methods. Thanks to techniques like control flow analysis, data flow analysis, partial evaluation, static single assignment, global value numbering, and liveness analysis, ProGuard can:
- Evaluate constant expressions.
- Remove unnecessary field accesses and method calls.
- Remove unnecessary branches.
- Remove unnecessary comparisons and instanceof tests.
- Remove unused code blocks.
- Merge identical code blocks.
- Reduce variable allocation.
- Remove write-only fields and unused method parameters.
- Inline constant fields, method parameters, and return values.
- Inline methods that are short or only called once.
- Simplify tail recursion calls.
- Merge classes and interfaces.
- Make methods private, static, and final when possible.
- Make classes static and final when possible.
- Replace interfaces that have single implementations.
- Perform over 200 peephole optimizations, like replacing ...*2 by ...<<1.
- Optionally remove logging code.
The positive effects of these optimizations will depend on your code and on the virtual machine on which the code is executed. Simple virtual machines may benefit more than advanced virtual machines with sophisticated JIT compilers. At the very least, your bytecode may become a bit smaller.
Some notable optimizations that aren't supported yet:
- Moving constant expressions out of loops.
- Optimizations that require escape analysis (DexGuard does).