java memory usage investigation

Abstract

Some tips to investigate memory usage:

  • generate a memory dump:
    • jmap -dump:[live],format=b,file=<file_path> <pid>
    • jcmd <pid> GC.heap_dump <file_path>
  • use Eclipse Memory Analyzer to read the memory dump file
  • use -Xmx with small value and let your app crash locally
  • use -XX:+HeapDumpOnOutOfMemoryError
  • use XX:StartFlightRecording=duration=200s which generates a JFR file, readable using JDK missing control
  • use pg_stat_statements extension:
# add to your postgresql.conf
shared_preload_libraries = 'pg_stat_statements'
CREATE EXTENSION pg_stat_statements;
-- don't forget to reset if you are debugging to have fresh stats
SELECT pg_stat_statements_reset();
  • use closed and opened projections by creating custom DTOs used as returned type in your JPA interfaces:
interface PersonRepository {
  // closed projections
  String getFirstName();
  String getLastName();
  PersonTrait getPersonTrait();
 
  record PersonFullName(String firstName, String lastName, Gender gender) {}
 
  // opened projections
  default String getFullname() {
    return getFirstName().concat(" ").concat(getLastName());
  }
}
  • get bulk of items:
@Repository
public interface PersonRepository extends JpaRepository<Person, UUID> {
  Stream<Person> getByFirstName(String firstName);
  @QueryHints(value = {
    @QueryHint(name = HINT_FETCH_SIZE, value = "300000")
  })
  Stream<Person> getByFirstName(String firstName);
  Page<Person> getByFirstName(String firstName, Pageable p);
  Slice<Person> getByFirstName(String firstName, Pageable p);
}
  • use hypersistence-utils to detect where the hibernate SQL query was executed (don’t forget to set the log level logging.level.io.hypersistence.utils.hybernate.query=DEBUG)
<dependency>
  <groupId>io.hypersistence</groupId>
  <artifactId>hypersistence-utils-hibernate-63</artifactId>
  <version>3.7.3</version>
</dependency>
  • add a cache
@EnableCaching
public class CacheConfiguration{}
 
@Repository
public interface PersonRepository extends JpaRepository<Person, UUID> {
  @Cacheable("personCache")
  List<Person> findByName(String name);
}