Improve Static Compilation Technology for Large Scale Java Workloads in Cloud
Cloud computing aims to provide computing resources as a service. The core principle set by cloud computing allows users to use only those resources that are necessary to run applications and scale when needed.
A microservices architecture breaks a monolith application into many micro-applications (microservices). That is an attractive approach to architect applications for cloud computing. With this, we can start with the number of microservices the users need at the beginning and scale-out more later when demand is higher, improving resilience by leveraging the ability of clouds to scale horizontally.
Over the years, Java has proliferated in Alibaba Cloud, and many programs are written in Java and developed as microservices for online trading, payments, and logistics operations, which are running on top of Kubernetes native environment to service online requests.
Unfortunately, long boot time in Java has inhibited horizontal scalability in the cloud. From a business standpoint, customers may have to wait a long time for an application to boot before the results of a request are received.
The Substrate compiled Java technology targets to compile the platform-independent Java bytecode application into a native executable file. That leads to a native code startup and would help to address the horizontal scaling challenge described above.
We have statically compiled a SOFA based application (https://github.com/sofastack/sofa-boot) with GraalVM native image technology and deployed them in the production environment to serve realistic online requests. Compared with the traditional JVM, the startup time significantly decreased from 60 seconds to 3 seconds.
However, there are still significant challenges in widely applying static compilation to large scale Java applications in the real world:
- The static compilation has the closed-world assumption(CWA), which requires all runtime information should be available at build time. Many dynamic features in Java are incompatible with this assumption. The most typical example is Java reflection. The reflection targets are only known at runtime in most cases, and it is hard for the static compilation to figure them out at build time. The current solution in the GraalVM native image is to take a beforehand run to record all touched reflections to a configuration file. The static compiler can obtain the reflection targets by reading the configuration file at build time. Though, this solution has a coverage problem because the beforehand run may not cover all reflections used in the production run. It may eventually lead to some unknown behaviors at runtime. A more reliable approach is required to solve such a problem.
- Generally, the static compilation performs a global Points-to analysis to find all reachable classes starting from the main entrance. That way, we can avoid compiling all classes included in the classpath. However, such static analysis might consume a large amount of memory and CPU resources. The analysis time and memory consumption increase significantly as the program size grows, which is exponentially correlated generally. The complexity of static analysis makes it tough to meet the quick dev-and-release cycle requirement in the cloud.
- Class initialization timing is a new challenge raised by static compilation technology. To further reduce the startup time, the Substrate compiled Java technology eagerly initializes classes at build time and stores the result in the generated native binary so that no class <clinit> check and initialization at runtime. Nevertheless, we could not do the eager initialization for all classes. For example, when the programmers put runtime behaviors such as starting threads, opening socket, and so forth in the class static initializer blocks, it is not safe to do such operations for these classes. How to safely preserve pre-initialized classes at build time is challenging to the current solution.
- A methodical model to improve the adaption of static compilation for large scale Java applications.
- A systematic approach to ensure the correctness of supporting Java dynamic features in the static compilation.
- An optimized mechanism to reduce the cost(cpu&memory) of the static compilation for large scale applications.
- A methodical model or prototype to automatically find and perform eager execution optimization at build time.
Related Research Topics
- Points to analysis
- Static analysis and compiler design
- Static compilation of Java application
Suggested Collaboration Method
AIR (Alibaba Innovative Research), one-year collaboration project.