DEV Community

Andrii Maliuta
Andrii Maliuta

Posted on • Edited on

GCP Serverless Function in Java with 15Mb RAM

The blog is more about experimenting with Java Native build to achieve less RAM use.

Usually, when using Java as a runtime for Google Cloud functions (https://cloud.google.com/functions/docs/concepts/java-runtime) the average RAM footprint is about 140 MB for a simple REST API Jax-RS based endpoint with JSON (text) static response. For example, the same example in Golang (one HTTP GET method) with JSON response is around 30 MB RAM.

To try to achieve less RAM footprint with Java (e.g. to use 128 MB GCP tier), let us try to use:

  1. Java Socket as very simple HTTP server
  2. GraalVM Native build
  3. Docker
  4. Google Cloud Run to use custom Docker image as an app

Java code

Source code: https://github.com/ahndmal/http-simple-sock-native

We create ServerSocket and accept connections on port 8080:

try (ServerSocket serverSocket = new ServerSocket(8080)) {
                System.out.println(">>> Server started on port 8080");

 while(true) {
    Socket accept = serverSocket.accept();

    System.out.println("> client connected");

    InputStream inputStream = accept.getInputStream();
    OutputStream outputStream = accept.getOutputStream();
   ...
}
Enter fullscreen mode Exit fullscreen mode

We create IO streams to get requests and write data to the output (browser) as a static HTTP GET response.

PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream));

writer.write("HTTP/1.1 200 OK\n");
writer.write("Content-Type: text/html\r\n\n");
writer.write("""
   <!doctype html><html>
   <head><title>Java</title></head>
   <body>
   <h2>Java Server</h2>
   <div>Test</div>
   </body>
   </html>""");
Enter fullscreen mode Exit fullscreen mode

We can just use any HTML file and read its contents for a response instead of this hardcoded text.

Docker image

We use 3 stages for Docker build:

  1. Build Jar file
  2. Use GraalVM findepi image to build native image from this Jar
  3. Use Debian slim to run the native app
# maven build
FROM maven:3.8.6-amazoncorretto-19 AS BUILD
COPY . .
RUN mvn clean package

# native build
FROM ghcr.io/graalvm/jdk:java
FROM findepi/graalvm:22.3.1-java19-native AS GRAAL
COPY --from=BUILD target/http-sock-3-0.0.2.jar /http-sock.jar
RUN gu install native-image && native-image -jar http-sock.jar

# run app
FROM debian:12-slim
#FROM debian:stable-slim
COPY --from=GRAAL /http-sock /tmp/http-sock
CMD ["/tmp/http-sock"]
Enter fullscreen mode Exit fullscreen mode

Build Docker image locally:

docker build --tag java-http-simple:0.0.1 .
Enter fullscreen mode Exit fullscreen mode

Tag docker image for GCP Artifact registry:

docker tag java-http-simple:0.0.1 us-east1-docker.pkg.dev/{PROJECT}/{ART_REG}/java-http-simple:0.0.2
Enter fullscreen mode Exit fullscreen mode

Deploy image from Artifact Registry to Cloud Run:

gcloud run deploy <service-name> --image <image_name>
Enter fullscreen mode Exit fullscreen mode

Metrics

Trigger the URL of the deployed Cloud Run service and check the results:

HTTP response:

  • 120 byte data
  • 160 ms response time

HTTP response

RAM metrics:

12% of RAM from 128MB service (~15.3 MB)

RAM Metrics

As we can see, the memory footprint is around 14MB.

Of course this is a silly app with a primitive response, but it is possible to use such an approach to create a simple REST API app with the DB data and JSON parsing with the little use of RAM.

Thank you for reading!

Top comments (0)