Neo4j’s heap and page cache are not just memory allocations; they are the primary determinants of your graph database’s performance and stability, and most people configure them in a way that cripples their instance.
Let’s watch Neo4j chew through some data. Imagine we have a graph of users and their connections, and we’re querying for friends of friends.
# Start Neo4j (assuming it's already installed and running)
# Let's say we have a large graph loaded, millions of nodes and relationships.
# Run a typical query that might stress memory
bin/neo4j-shell -d /var/lib/neo4j/data/databases/neo4j
> MATCH (u:User {userId: 'user123'})-[:FRIENDS_WITH]->(f)-[:FRIENDS_WITH]->(ff:User)
> RETURN DISTINCT ff.userId
> LIMIT 100
As this query runs, Neo4j is doing a lot of work behind the scenes. It needs to find user123, traverse its FRIENDS_WITH relationships, find those f nodes, and then traverse their FRIENDS_WITH relationships to find ff nodes. This involves reading data from disk, and if that data is already in memory, it’s much faster. This is where the heap and page cache come in.
The heap is where Neo4j Java objects live: query results, traversal state, internal data structures for query execution. The page cache is Neo4j’s primary mechanism for keeping frequently accessed graph data (nodes, relationships, properties) in RAM, directly mapped from disk. Neo4j uses memory mapping for its data files, and the page cache holds these mapped pages.
The key to tuning is understanding the relationship between your total available RAM, the heap size, and the page cache size.
Heap Configuration:
The Neo4j heap is configured via conf/neo4j.conf by setting dbms.memory.heap.initial_size and dbms.memory.heap.max_size.
- Diagnosis: If you see
java.lang.OutOfMemoryError: Java heap spacein yourneo4j.log, your heap is too small for the active Java objects. - Cause 1: Heap too small for active query processing.
- Check: Monitor JVM heap usage with
jstat -gc <neo4j_pid>. Look for consistently high usage and frequent Full GCs. - Fix: Increase
dbms.memory.heap.initial_sizeanddbms.memory.heap.max_size. For a 32GB RAM server, a common starting point might be:dbms.memory.heap.initial_size=16G dbms.memory.heap.max_size=16G - Why it works: This allocates more contiguous memory for Java objects, preventing the JVM from running out of space during complex query execution or large result set manipulation.
- Check: Monitor JVM heap usage with
- Cause 2: Heap too large, starving the page cache.
- Check: If
dbms.memory.heap.max_sizeis set too high, it leaves insufficient RAM for the OS page cache. This leads to excessive disk I/O. - Fix: Reduce
dbms.memory.heap.max_size. A common rule of thumb is to allocate no more than 50% of total RAM to the heap. If you have 32GB RAM, keep heap max at16G. - Why it works: By reducing the heap, you free up RAM for the OS to use as a page cache, allowing more frequently accessed graph data to reside in memory, reducing disk reads.
- Check: If
Page Cache Configuration:
The page cache size is primarily managed by the operating system, but Neo4j influences it by how much memory it doesn’t request for its heap. Neo4j also has a direct memory mapping setting, dbms.memory.pagecache.size. This setting is a maximum for the page cache, and the OS can use additional RAM for its own page caching.
- Diagnosis: Slow query performance, high disk I/O (e.g.,
iostatshows high%utilorawaitfor your data disk), andneo4j.logshowing messages about slow reads or cache misses. - Cause 1: Page cache too small for the working dataset.
- Check: Monitor page cache usage. While direct OS tools are best, Neo4j provides metrics. In Neo4j Browser, run
:sysinfo, and look at "pagecache.mappedMemory" and "pagecache.usedMemory". IfusedMemoryis consistently nearmappedMemoryand performance is poor, your working set doesn’t fit. - Fix: Increase
dbms.memory.pagecache.size. On a 32GB RAM server with16Gheap, you might set:dbms.memory.pagecache.size=12G - Why it works: This tells Neo4j to map a larger portion of its data files into memory, allowing the OS to fill this larger mapped region with frequently accessed data pages, reducing the need to fetch them from disk. The remaining
4G(32GB total - 16GB heap - 12GB pagecache) is available for OS overhead and other processes.
- Check: Monitor page cache usage. While direct OS tools are best, Neo4j provides metrics. In Neo4j Browser, run
- Cause 2: Page cache configured too high, starving the heap.
- Check: If you’ve set
dbms.memory.pagecache.sizetoo aggressively, you might start seeing heapOutOfMemoryErrors or very frequent, long GC pauses as the JVM struggles to find space. - Fix: Decrease
dbms.memory.pagecache.size. If you have16Gheap on a 32GB server, and you’re seeing heap issues, try reducing the page cache:dbms.memory.pagecache.size=10G - Why it works: This frees up RAM for the JVM heap, giving it more breathing room for object allocation and reducing the frequency and duration of garbage collection cycles.
- Check: If you’ve set
General Memory Allocation Strategy:
A good starting point for a dedicated Neo4j server with X GB of RAM is:
- Heap:
X / 2GB (e.g., 16GB for 32GB RAM). Setinitialandmaxto the same value. - Page Cache:
(X - (X / 2)) - 2GB(e.g., (32GB - 16GB) - 2GB = 14GB. Wait, I said 12G above. Let’s correct that. For 32GB total: 16GB heap, leaves 16GB for page cache and OS. If we set page cache to 14GB, that leaves 2GB for OS. So,dbms.memory.pagecache.size=14G). - OS/Other: The remaining RAM (e.g., 2GB in the 32GB example).
This leaves ample space for the OS and other system processes. Always leave at least 1-2GB for the OS.
The exact values depend on your graph size, query patterns, and hardware. Monitor your system closely after making changes.
After correctly tuning your memory, the next common bottleneck you’ll encounter is disk I/O for very large datasets that still don’t fit entirely in the page cache, leading you to explore storage hardware and RAID configurations.