How Quarkus use build time to start your application faster
Tags: quarkus, cloud, java, optimization
Published: 2023-02-28 22:31:08

In this topic, we will try to undestand basics of how Quarkus augments application. We will cover how build process works, structure of resulted artifact and how it is launched.

## What is Quarkus
Quarkus is framework for Java (primary) and Kotlin with focus on the cloud development.

## What are benefits of Quarkus
* Quick startup
* Low memory consumption
* Build time DI resolution

## How Quarkus build application
At build time (also referred to as deployment time), Quarkus does most of the work. Let's establish the basics of how Quarkus builds our application.

### Quarkus application build flow
Once all application classes are compiled, Quarkus build starts. During bootstrap, it prepares for the build, e.g. resolves all application dependencies. Next, Quarkus performs deployment (build) and produces an artifact. Let's take a closer look at each step related to Quarkus.

![Quarkus application build flow (JVM mode)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/csf02hydcgse6cf9hzjr.png)

### Bootstrap phase
Before run build Quarkus does:
* Resolve application dependencies.
* Build build chain.
* Produce initial build items into build chain.

### Core build components
Build process consists from two basic blocks build step and build item.
#### Build item
They can be:
* initial and non-initial
* simple and multi

#### Initial Build Items
Initial build items are created and passed to the build chain by Quarkus and cannot be produced by build steps.
For example, the `LaunchModeBuildItem` indicates the launch type, such as production, development, or testing.

#### Simple build item
Can only be produced in one instance (and by only one **build step**)
For example, the `CombinedIndexBuildItem` contains index, which build from the application classes and dependencies that contain a certain marker file.

##### Multi build item
Can be produced any amount of time by any amount of **build steps**
For example, the `AdditionalBeanBuildItem` is produced by extensions to dynamically specify a bean.

#### Build step
* Method with `@BuildStep` annotation.
* Consume build items.
* Produce build items by returning a value from a method or using `BuildProducer<BuildItem>`
* Can be recorded and invoked at application startup.

#### Build step recording
The build step recording process works as follows:

1. A proxy is created for the recorder passed in method using an `InvocationHandler`
2. The proxy records all invocation information.
3. Also proxy is created for all returned values from the invoked methods.
4. Bytecode to be invoked at application startup is recorded based on the information gathered by the proxies.
5. Recorded bytecode produced as `StaticBytecodeRecorderBuildItem` or `MainBytecodeRecorderBuildItem` and collected in `MainClassBuildStep` where runner class is generated.

`InvocationHandler` dummy implementation:
```java
new InvocationHandler() {	
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // do something on method invocation
  }
}
```
### (Simplified) Build process
#### Build chain model
The resulting build chain can be represented as an oriented graph, with build items and build steps as nodes. Quarkus determines which build steps do not depend on any other build steps and starts their execution. After they have finished, it counts down the counter at the dependent build steps; if the counter equals 0, they start execution. All executions are done in parallel.
#### Pre DI build phase
![Pre DI build phase](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rmzxm0gulu1k9ti5fkor.png)
This scheme illustrates how Quarkus collects bean archives for dependency injection (DI). The process begins by collecting all archives, regardless of whether they contain beans or not. Next, Quarkus selects the bean archives from the collected archives.

Initially, collect all application classes. In case of a multi-module application, only classes in the main application module are indexed at this step, while others are treated as dependencies.

Next, the `ApplicationArchiveBuildStep` processes all application dependencies. Dependencies are indexed if they meet at least one of the following criteria:

* Dependency contains one of the specified marker files, such as `META-INF/jandex.idx` or `META-INF/beans.xml`. Additional marker files can be specified using the `AdditionalApplicationArchiveMarkerBuildItem`
* Dependency is included in one of the produced `IndexDependencyBuildItem`
* Dependency is included in one of the initial `AdditionalApplicationArchiveBuildItem` build items, which cannot be produced by the user.

Also Quarkus index all classes specified in `AdditionalIndexedClassBuildItem`

These archives combined into `CombinedIndexBuildItem`, which consumed by `BeanArchiveProcessor`

Then `BeanArchiveProcessor` create bean archives index. Archive is treated as a bean archive if it meets at least one of the following criteria:
* Archive contains `META-INF/beans.xml`
* Archive contains at least one class with bean defining annotation, for example `@ApplicationScoped`
* Archive specified as bean archive in `BeanArchivePredicateBuildItem`

#### DI build phase
During build time, Quarkus performs the following tasks:
* Resolves all injection points.
* Generates [static proxies](https://dev.to/nutrymaco/static-proxies-in-quarkus-4ebf) for beans to avoid reflection.
* Generates bytecode to set up DI at application startup.

#### Create artifact phase
Result artifact generated in `JarResultBuildStep`. Quarkus has several artifact formats. For JVM default and recommended is **fast-jar**. It has followed structure.

![Build a structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ssz6ig22az2auf0dydsf.png)


## How Quarkus starts application
Quarkus offers several launch modes, but here we will focus only on the production mode.

To start the application in production mode, launch the `target/quarkus-app/quarkus-run.jar` JAR. The QuarkusEntryPoint main class will be loaded from `lib/boot/`

The next step is for `QuarkusEntryPoint` to read the `quarkus-application.dat` file, from which it creates the `RunnerClassLoader`, the class loader for the Quarkus application. Using the information from `quarkus-application.dat`, the `RunnerClassLoader` will attempt to load a class from the parent class loader (JVM class loader) or from indexed jars, which is faster than the JVM class loader.

Then, the generated `ApplicationImpl` runs, where the static block contains static recordings, and the start method contains runtime recordings. It looks something like this:
```java
// $FF: synthetic class
public class ApplicationImpl extends Application {
  static Logger LOG;
  public static StartupContext STARTUP_CONTEXT;

  public ApplicationImpl() {
    super(false);
  }

  static {
  // invoke static recordings
  }

  protected final void doStart(String[] var1) {
    // invoke runtime recordings
  }

  protected final void doStop() {
    STARTUP_CONTEXT.close();
  }

  public String getName() {
    return "simple-app";
  }
}
```
## Summary
We covered class indexing, build-time preparation for dependency injection, artifact structure, and application startup. While there are other important topics such as native executable creation, more advanced overview of dependency injection, and server implementation, it would be best to address them separately.