Giorgio Arlandi
Giorgio Arlandi

Reputation: 23

Spring Boot application on Heroku consistently hits memory limit—how do I reduce usage?

memory graph

I have a Spring Boot application deployed on Heroku (Free or Hobby dyno, with ~1GB RAM). The problem is that it consistently uses up to the memory limit, often around 91–92%, and I end up having to frequently restart the dynos just to keep things running.

Below is my current build.gradle file (Kotlin + Spring Boot):

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile

buildscript {
    ext.kotlin_version = '1.8.0'
    dependencies {
        classpath("org.jetbrains.kotlin:kotlin-noarg:1.6.21")
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
    repositories {
        mavenCentral()
    }
}

plugins {
    id 'org.springframework.boot' version '2.7.9'
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
    id 'org.jetbrains.kotlin.jvm' version '1.6.21'
    id 'org.jetbrains.kotlin.plugin.spring' version '1.6.21'
    id "org.jetbrains.kotlin.plugin.allopen" version "1.9.21"
}

apply plugin: "kotlin-jpa"
apply plugin: 'kotlin'

group = 'cl.arlanditech'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_11

repositories {
    mavenCentral()
}

dependencies {
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
    implementation 'org.jetbrains.kotlin:kotlin-reflect'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'

    implementation 'com.auth0:java-jwt:4.3.0'
    implementation 'io.jsonwebtoken:jjwt:0.9.1'
    implementation 'javax.xml.bind:jaxb-api:2.3.1'
    implementation 'io.projectreactor:reactor-core:3.5.4'
    implementation "com.stripe:stripe-java:24.0.0"
    implementation 'org.postgresql:postgresql:42.6.0'
    implementation 'org.springframework.boot:spring-boot-starter-jdbc:2.7.9'
    implementation 'com.zaxxer:HikariCP:5.1.0'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'com.restfb:restfb:2023.16.0'
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'com.google.api-client:google-api-client:2.2.0'
    implementation 'org.springframework.boot:spring-boot-starter-cache'
    implementation 'com.github.ben-manes.caffeine:caffeine'
    implementation 'org.apache.pdfbox:pdfbox:3.0.3'
    implementation 'org.springframework.retry:spring-retry:2.0.5'
    implementation 'org.springframework:spring-aspects'
    implementation 'org.springframework:spring-context-support'
    implementation 'org.springframework.boot:spring-boot-starter-mail'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation "com.aallam.openai:openai-client:3.8.2"
    implementation 'io.ktor:ktor-client-cio:2.3.7'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.7.3'
    implementation 'com.resend:resend-java:3.1.0'
    implementation 'com.sendgrid:sendgrid-java:4.10.1'
    implementation 'com.google.firebase:firebase-admin:9.2.0'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    implementation 'com.tinify:tinify:latest.release'
    implementation 'com.google.cloud:google-cloud-retail:2.56.0'
}

tasks.withType(KotlinCompile).configureEach {
    kotlinOptions {
        freeCompilerArgs = ['-Xjsr305=strict']
        jvmTarget = '1.8'
    }
}

tasks.named('test') {
    useJUnitPlatform()
}

tasks.named('compileJava') {
    inputs.files(tasks.named('processResources'))
}

allOpen {
    annotation("javax.persistence.Entity")
    annotation("javax.persistence.Embeddable")
    annotation("javax.persistence.MappedSuperclass")
}

tasks.withType(KotlinJvmCompile).configureEach {
    compilerOptions {
        jvmTarget.set(JvmTarget.JVM_11)
        freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn")
    }
}

Questions

I’d appreciate any insights or suggestions on how to profile this or what configuration changes to make.

Thank you in advance!

Upvotes: 0

Views: 50

Answers (1)

sreedhar honnala
sreedhar honnala

Reputation: 1

As others mentioned, it will be tough to understand the problem without heap dump. You can verify below and if any of them is true that may be one of the causes to the issue.

  1. Is there any objects being initialised during application startup. If yes check the size of the objects being loaded and avoid loding high payload into memory.
  2. Are you loading any objects in memory and adding objects to the same objects which kept in memory.
  3. There is possibility that any third party library could have stored high payload in memory. If that's the case then you might not be using it correctly.

However, I would still recommend to capture the heap dump and check the area which is consuming high memory and handle.

If it is feasible to connect to visualvm connect to it and there you can analyse or capture heap dump and analyse it using Eclipse-MAT app.

May be this link helps in capturing heap dump: How to take heap dump?

Hope this helps to understand the problem.

Upvotes: 0

Related Questions