java memory usage investigation
src: Il faut sauver le dernier giga de RAM - 2024-12-05
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 levellogging.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);
}