Skip to content

Commit 1367b3d

Browse files
IanButterworthadienesgbaraldid-netto
authored
Docs: add GC user docs (#58733)
Co-authored-by: Andy Dienes <51664769+adienes@users.noreply.github.com> Co-authored-by: Gabriel Baraldi <28694980+gbaraldi@users.noreply.github.com> Co-authored-by: Diogo Netto <61364108+d-netto@users.noreply.github.com>
1 parent 6ddb3d6 commit 1367b3d

File tree

5 files changed

+186
-4
lines changed

5 files changed

+186
-4
lines changed

doc/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ Manual = [
192192
"manual/code-loading.md",
193193
"manual/profile.md",
194194
"manual/stacktraces.md",
195+
"manual/memory-management.md",
195196
"manual/performance-tips.md",
196197
"manual/workflow-tips.md",
197198
"manual/style-guide.md",

doc/src/manual/command-line-interface.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ The following is a complete list of command-line switches available when launchi
180180
|`-m`, `--module <Package> [args]` |Run entry point of `Package` (`@main` function) with `args`|
181181
|`-L`, `--load <file>` |Load `<file>` immediately on all processors|
182182
|`-t`, `--threads {auto\|N[,auto\|M]}` |Enable N[+M] threads; N threads are assigned to the `default` threadpool, and if M is specified, M threads are assigned to the `interactive` threadpool; `auto` tries to infer a useful default number of threads to use but the exact behavior might change in the future. Currently sets N to the number of CPUs assigned to this Julia process based on the OS-specific affinity assignment interface if supported (Linux and Windows) or to the number of CPU threads if not supported (MacOS) or if process affinity is not configured, and sets M to 1.|
183-
| `--gcthreads=N[,M]` |Use N threads for the mark phase of GC and M (0 or 1) threads for the concurrent sweeping phase of GC. N is set to the number of compute threads and M is set to 0 if unspecified.|
183+
| `--gcthreads=N[,M]` |Use N threads for the mark phase of GC and M (0 or 1) threads for the concurrent sweeping phase of GC. N is set to the number of compute threads and M is set to 0 if unspecified. See [Memory Management and Garbage Collection](@ref man-memory-management) for more details.|
184184
|`-p`, `--procs {N\|auto}` |Integer value N launches N additional local worker processes; `auto` launches as many workers as the number of local CPU threads (logical cores)|
185185
|`--machine-file <file>` |Run processes on hosts listed in `<file>`|
186186
|`-i`, `--interactive` |Interactive mode; REPL runs and `isinteractive()` is true|
@@ -206,7 +206,7 @@ The following is a complete list of command-line switches available when launchi
206206
|`--track-allocation=@<path>` |Count bytes but only in files that fall under the given file path/directory. The `@` prefix is required to select this option. A `@` with no path will track the current directory.|
207207
|`--task-metrics={yes\|no*}` |Enable the collection of per-task metrics|
208208
|`--bug-report=KIND` |Launch a bug report session. It can be used to start a REPL, run a script, or evaluate expressions. It first tries to use BugReporting.jl installed in current environment and falls back to the latest compatible BugReporting.jl if not. For more information, see `--bug-report=help`.|
209-
|`--heap-size-hint=<size>` |Forces garbage collection if memory usage is higher than the given value. The value may be specified as a number of bytes, optionally in units of KB, MB, GB, or TB, or as a percentage of physical memory with %.|
209+
|`--heap-size-hint=<size>` |Forces garbage collection if memory usage is higher than the given value. The value may be specified as a number of bytes, optionally in units of KB, MB, GB, or TB, or as a percentage of physical memory with %. See [Memory Management and Garbage Collection](@ref man-memory-management) for more details.|
210210
|`--compile={yes*\|no\|all\|min}` |Enable or disable JIT compiler, or request exhaustive or minimal compilation|
211211
|`--output-o <name>` |Generate an object file (including system image data)|
212212
|`--output-ji <name>` |Generate a system image data file (.ji)|

doc/src/manual/memory-management.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# [Memory Management and Garbage Collection](@id man-memory-management)
2+
3+
Julia uses automatic memory management through its built-in garbage collector (GC). This section provides an overview of how Julia manages memory and how you can configure and optimize memory usage for your applications.
4+
5+
## [Garbage Collection Overview](@id man-gc-overview)
6+
7+
Julia features a garbage collector with the following characteristics:
8+
9+
* **Non-moving**: Objects are not relocated in memory during garbage collection
10+
* **Generational**: Younger objects are collected more frequently than older ones
11+
* **Parallel and partially concurrent**: The GC can use multiple threads and run concurrently with your program
12+
* **Mostly precise**: The GC accurately identifies object references for pure Julia code, and it provides conservative scanning APIs for users calling Julia from C
13+
14+
The garbage collector automatically reclaims memory used by objects that are no longer reachable from your program, freeing you from manual memory management in most cases.
15+
16+
## [Memory Architecture](@id man-memory-architecture)
17+
18+
Julia uses a two-tier allocation strategy:
19+
20+
* **Small objects** (currently ≤ 2032 bytes but may change): Allocated using a fast per-thread pool allocator
21+
* **Large objects** : Allocated directly through the system's `malloc`
22+
23+
This hybrid approach optimizes for both allocation speed and memory efficiency, with the pool allocator providing fast allocation for the many small objects typical in Julia programs.
24+
25+
## [System Memory Requirements](@id man-system-memory)
26+
27+
### Swap Space
28+
29+
Julia's garbage collector is designed with the expectation that your system has adequate swap space configured. The GC uses heuristics that assume it can allocate memory beyond physical RAM when needed, relying on the operating system's virtual memory management.
30+
31+
If your system has limited or no swap space, you may experience out-of-memory errors during garbage collection. In such cases, you can use the `--heap-size-hint` option to limit Julia's memory usage.
32+
33+
### Memory Hints
34+
35+
You can provide a hint to Julia about the maximum amount of memory to use:
36+
37+
```bash
38+
julia --heap-size-hint=4G # To set the hint to ~4GB
39+
julia --heap-size-hint=50% # or to 50% of physical memory
40+
```
41+
42+
The `--heap-size-hint` option tells the garbage collector to trigger collection more aggressively when approaching the specified limit. This is particularly useful in:
43+
44+
* Containers with memory limits
45+
* Systems without swap space
46+
* Shared systems where you want to limit Julia's memory footprint
47+
48+
You can also set this via the `JULIA_HEAP_SIZE_HINT` environment variable:
49+
50+
```bash
51+
export JULIA_HEAP_SIZE_HINT=2G
52+
julia
53+
```
54+
55+
## [Multithreaded Garbage Collection](@id man-gc-multithreading)
56+
57+
Julia's garbage collector can leverage multiple threads to improve performance on multi-core systems.
58+
59+
### GC Thread Configuration
60+
61+
By default, Julia uses multiple threads for garbage collection:
62+
63+
* **Mark threads**: Used during the mark phase to trace object references (default: 1, which is shared with the compute thread if there is only one, otherwise half the number of compute threads)
64+
* **Sweep threads**: Used for concurrent sweeping of freed memory (default: 0, disabled)
65+
66+
You can configure GC threading using:
67+
68+
```bash
69+
julia --gcthreads=4,1 # 4 mark threads, 1 sweep thread
70+
julia --gcthreads=8 # 8 mark threads, 0 sweep threads
71+
```
72+
73+
Or via environment variable:
74+
75+
```bash
76+
export JULIA_NUM_GC_THREADS=4,1
77+
julia
78+
```
79+
80+
### Recommendations
81+
82+
For compute-intensive workloads:
83+
84+
* Use multiple mark threads (the default configuration is usually appropriate)
85+
* Consider enabling concurrent sweeping with 1 sweep thread for allocation-heavy workloads
86+
87+
For memory-intensive workloads:
88+
89+
* Enable concurrent sweeping to reduce GC pauses
90+
* Monitor GC time using `@time` and adjust thread counts accordingly
91+
92+
## [Monitoring and Debugging](@id man-gc-monitoring)
93+
94+
### Basic Memory Monitoring
95+
96+
Use the `@time` macro to see memory allocation and GC overhead:
97+
98+
```julia
99+
julia> @time some_computation()
100+
2.123456 seconds (1.50 M allocations: 58.725 MiB, 17.17% gc time)
101+
```
102+
103+
### GC Logging
104+
105+
Enable detailed GC logging to understand collection patterns:
106+
107+
```julia
108+
julia> GC.enable_logging(true)
109+
julia> # Run your code
110+
julia> GC.enable_logging(false)
111+
```
112+
113+
This logs each garbage collection event with timing and memory statistics.
114+
115+
### Manual GC Control
116+
117+
While generally not recommended, you can manually trigger garbage collection:
118+
119+
```julia
120+
GC.gc() # Force a garbage collection
121+
GC.enable(false) # Disable automatic GC (use with caution!)
122+
GC.enable(true) # Re-enable automatic GC
123+
```
124+
125+
**Warning**: Disabling GC can lead to memory exhaustion. Only use this for specific performance measurements or debugging.
126+
127+
## [Performance Considerations](@id man-gc-performance)
128+
129+
### Reducing Allocations
130+
131+
The best way to minimize GC impact is to reduce unnecessary allocations:
132+
133+
* Use in-place operations when possible (e.g., `x .+= y` instead of `x = x + y`)
134+
* Pre-allocate arrays and reuse them
135+
* Avoid creating temporary objects in tight loops
136+
* Consider using `StaticArrays.jl` for small, fixed-size arrays
137+
138+
### Memory-Efficient Patterns
139+
140+
* Avoid global variables that change type
141+
* Use `const` for global constants
142+
143+
### Profiling Memory Usage
144+
145+
For detailed guidance on profiling memory allocations and identifying performance bottlenecks, see the [Profiling](@ref man-profiling) section.
146+
147+
## [Advanced Configuration](@id man-gc-advanced)
148+
149+
### Integration with System Memory Management
150+
151+
Julia works best when:
152+
153+
* The system has adequate swap space (recommended: 2x physical RAM)
154+
* Virtual memory is properly configured
155+
* Other processes leave sufficient memory available
156+
* Container memory limits are set appropriately with `--heap-size-hint`
157+
158+
## [Troubleshooting Memory Issues](@id man-gc-troubleshooting)
159+
160+
### High GC Overhead
161+
162+
If garbage collection is taking too much time:
163+
164+
1. **Reduce allocation rate**: Focus on algorithmic improvements
165+
2. **Adjust GC threads**: Experiment with different `--gcthreads` settings
166+
3. **Use concurrent sweeping**: Enable background sweeping with `--gcthreads=N,1`
167+
4. **Profile memory patterns**: Identify allocation hotspots and optimize them
168+
169+
### Memory Leaks
170+
171+
While Julia's GC prevents most memory leaks, issues can still occur:
172+
173+
* **Global references**: Avoid holding references to large objects in global variables
174+
* **Closures**: Be careful with closures that capture large amounts of data
175+
* **C interop**: Ensure proper cleanup when interfacing with C libraries
176+
177+
For more detailed information about Julia's garbage collector internals, see the Garbage Collection section in the Developer Documentation.

doc/src/manual/multi-threading.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,15 @@ julia> Threads.threadid()
8484

8585
### Multiple GC Threads
8686

87-
The Garbage Collector (GC) can use multiple threads. The amount used is either half the number
88-
of compute worker threads or configured by either the `--gcthreads` command line argument or by using the
87+
The Garbage Collector (GC) can use multiple threads. The amount used by default matches the compute
88+
worker threads or can configured by either the `--gcthreads` command line argument or by using the
8989
[`JULIA_NUM_GC_THREADS`](@ref JULIA_NUM_GC_THREADS) environment variable.
9090

9191
!!! compat "Julia 1.10"
9292
The `--gcthreads` command line argument requires at least Julia 1.10.
9393

94+
For more details about garbage collection configuration and performance tuning, see [Memory Management and Garbage Collection](@ref man-memory-management).
95+
9496
## [Threadpools](@id man-threadpools)
9597

9698
When a program's threads are busy with many tasks to run, tasks may experience

doc/src/manual/performance-tips.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ Consequently, in addition to the allocation itself, it's very likely
116116
that the code generated for your function is far from optimal. Take such indications seriously
117117
and follow the advice below.
118118

119+
For more information about memory management and garbage collection in Julia, see [Memory Management and Garbage Collection](@ref man-memory-management).
120+
119121
In this particular case, the memory allocation is due to the usage of a type-unstable global variable `x`, so if we instead pass `x` as an argument to the function it no longer allocates memory
120122
(the remaining allocation reported below is due to running the `@time` macro in global scope)
121123
and is significantly faster after the first call:

0 commit comments

Comments
 (0)