From fd9214e242f18bdc185299c8ec53282c3970daad Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sat, 14 Jun 2025 17:34:37 -0400 Subject: [PATCH 1/7] add GC docs --- doc/make.jl | 1 + doc/src/manual/command-line-interface.md | 4 +- doc/src/manual/memory-management.md | 203 +++++++++++++++++++++++ doc/src/manual/multi-threading.md | 2 + doc/src/manual/performance-tips.md | 2 + 5 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 doc/src/manual/memory-management.md diff --git a/doc/make.jl b/doc/make.jl index da78a8420fa2c..02dd53984b079 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -161,6 +161,7 @@ generate_markdown("NEWS") Manual = [ "manual/getting-started.md", "manual/installation.md", + "manual/memory-management.md", "manual/variables.md", "manual/integers-and-floating-point-numbers.md", "manual/mathematical-operations.md", diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index 6cbb215a2ded5..e443ff2f0fab4 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -180,7 +180,7 @@ The following is a complete list of command-line switches available when launchi |`-m`, `--module [args]` |Run entry point of `Package` (`@main` function) with `args'| |`-L`, `--load ` |Load `` immediately on all processors| |`-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.| -| `--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.| +| `--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.| |`-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)| |`--machine-file ` |Run processes on hosts listed in ``| |`-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 |`--track-allocation=@` |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.| |`--task-metrics={yes\|no*}` |Enable the collection of per-task metrics| |`--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`.| -|`--heap-size-hint=` |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 %.| +|`--heap-size-hint=` |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.| |`--compile={yes*\|no\|all\|min}` |Enable or disable JIT compiler, or request exhaustive or minimal compilation| |`--output-o ` |Generate an object file (including system image data)| |`--output-ji ` |Generate a system image data file (.ji)| diff --git a/doc/src/manual/memory-management.md b/doc/src/manual/memory-management.md new file mode 100644 index 0000000000000..30525c0b33f05 --- /dev/null +++ b/doc/src/manual/memory-management.md @@ -0,0 +1,203 @@ +# [Memory Management and Garbage Collection](@id man-memory-management) + +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. + +## [Garbage Collection Overview](@id man-gc-overview) + +Julia features a garbage collector with the following characteristics: + +* **Non-moving**: Objects are not relocated in memory during garbage collection +* **Generational**: Younger objects are collected more frequently than older ones +* **Parallel and partially concurrent**: The GC can use multiple threads and run concurrently with your program +* **Mostly precise**: The GC accurately identifies object references with minimal conservative scanning + +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. + +## [Memory Architecture](@id man-memory-architecture) + +Julia uses a two-tier allocation strategy: + +* **Small objects** (≤ 2032 bytes): Allocated using a fast per-thread pool allocator +* **Large objects** (> 2032 bytes): Allocated directly through the system's `malloc` + +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. + +## [System Memory Requirements](@id man-system-memory) + +### Swap Space + +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. + +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. + +### Memory Hints + +You can provide a hint to Julia about the maximum amount of memory to use: + +```bash +julia --heap-size-hint=4G # Limit heap to ~4GB +julia --heap-size-hint=50% # Use up to 50% of physical memory +``` + +The `--heap-size-hint` option tells the garbage collector to trigger collection more aggressively when approaching the specified limit. This is particularly useful in: + +* Containers with memory limits +* Systems without swap space +* Shared systems where you want to limit Julia's memory footprint + +You can also set this via the `JULIA_HEAP_SIZE_HINT` environment variable: + +```bash +export JULIA_HEAP_SIZE_HINT=2G +julia +``` + +## [Multithreaded Garbage Collection](@id man-gc-multithreading) + +Julia's garbage collector can leverage multiple threads to improve performance on multi-core systems. + +### GC Thread Configuration + +By default, Julia uses multiple threads for garbage collection: + +* **Mark threads**: Used during the mark phase to trace object references (default: same as compute threads (thread 1 is shared)) +* **Sweep threads**: Used for concurrent sweeping of freed memory (default: 0, disabled) + +You can configure GC threading using: + +```bash +julia --gcthreads=4,1 # 4 mark threads, 1 sweep thread +julia --gcthreads=8 # 8 mark threads, 0 sweep threads +``` + +Or via environment variable: + +```bash +export JULIA_NUM_GC_THREADS=4,1 +julia +``` + +### Recommendations + +For compute-intensive workloads: + +* Use multiple mark threads (the default configuration is usually appropriate) +* Consider enabling concurrent sweeping with 1 sweep thread for allocation-heavy workloads + +For memory-intensive workloads: + +* Enable concurrent sweeping to reduce GC pauses +* Monitor GC time using `@time` and adjust thread counts accordingly + +## [Monitoring and Debugging](@id man-gc-monitoring) + +### Basic Memory Monitoring + +Use the `@time` macro to see memory allocation and GC overhead: + +```julia +julia> @time some_computation() + 2.123456 seconds (1.50 M allocations: 58.725 MiB, 17.17% gc time) +``` + +This shows: + +* Total execution time +* Number of allocations +* Memory allocated +* Percentage of time spent in garbage collection + +### GC Logging + +Enable detailed GC logging to understand collection patterns: + +```julia +julia> GC.enable_logging(true) +julia> # Run your code +julia> GC.enable_logging(false) +``` + +This logs each garbage collection event with timing and memory statistics. + +### Manual GC Control + +While generally not recommended, you can manually trigger garbage collection: + +```julia +GC.gc() # Force a garbage collection +GC.enable(false) # Disable automatic GC (use with caution!) +GC.enable(true) # Re-enable automatic GC +``` + +**Warning**: Disabling GC can lead to memory exhaustion. Only use this for specific performance measurements or debugging. + +## [Performance Considerations](@id man-gc-performance) + +### Reducing Allocations + +The best way to minimize GC impact is to reduce unnecessary allocations: + +* Use in-place operations when possible (e.g., `x .+= y` instead of `x = x + y`) +* Pre-allocate arrays and reuse them +* Avoid creating temporary objects in tight loops +* Consider using `StaticArrays.jl` for small, fixed-size arrays + +### Memory-Efficient Patterns + +* Use `Vector{Float64}` instead of `Vector{Any}` when possible +* Avoid global variables that change type +* Use `const` for global constants +* Consider memory pools for frequently allocated/freed objects + +### Profiling Memory Usage + +For detailed guidance on profiling memory allocations and identifying performance bottlenecks, see the [Profiling](@ref man-profiling) section. + +## [Advanced Configuration](@id man-gc-advanced) + +### Environment Variables + +Additional GC-related environment variables: + +* `JULIA_GC_WAIT_FOR_DEBUGGER`: Pause for debugger attachment during GC (debug builds only) +* `JULIA_PROFILE_PEEK_HEAP_SNAPSHOT`: Enable heap snapshot collection during profiling + +### Integration with System Memory Management + +Julia works best when: + +* The system has adequate swap space (recommended: 2x physical RAM) +* Virtual memory is properly configured +* Other processes leave sufficient memory available +* Container memory limits are set appropriately with `--heap-size-hint` + +## [Troubleshooting Memory Issues](@id man-gc-troubleshooting) + +### Out of Memory Errors + +If you encounter out-of-memory errors: + +1. **Check system memory**: Ensure adequate RAM and swap space +2. **Use heap size hints**: Set `--heap-size-hint` to limit memory usage +3. **Profile allocations**: Identify and reduce unnecessary allocations +4. **Consider chunking**: Process data in smaller chunks for large datasets + +### High GC Overhead + +If garbage collection is taking too much time: + +1. **Reduce allocation rate**: Focus on algorithmic improvements +2. **Adjust GC threads**: Experiment with different `--gcthreads` settings +3. **Use concurrent sweeping**: Enable background sweeping with `--gcthreads=N,1` +4. **Profile memory patterns**: Identify allocation hotspots and optimize them + +### Memory Leaks + +While Julia's GC prevents most memory leaks, issues can still occur: + +* **Global references**: Avoid holding references to large objects in global variables +* **Closures**: Be careful with closures that capture large amounts of data +* **C interop**: Ensure proper cleanup when interfacing with C libraries +* **Circular references**: While handled by the GC, they can delay collection + +For more detailed information about Julia's garbage collector internals, see the Garbage Collection section in the Developer Documentation. diff --git a/doc/src/manual/multi-threading.md b/doc/src/manual/multi-threading.md index 401169e9c2132..83852726f015b 100644 --- a/doc/src/manual/multi-threading.md +++ b/doc/src/manual/multi-threading.md @@ -91,6 +91,8 @@ of compute worker threads or configured by either the `--gcthreads` command line !!! compat "Julia 1.10" The `--gcthreads` command line argument requires at least Julia 1.10. +For more details about garbage collection configuration and performance tuning, see [Memory Management and Garbage Collection](@ref man-memory-management). + ## [Threadpools](@id man-threadpools) When a program's threads are busy with many tasks to run, tasks may experience diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index d506ac9946ba6..b2a366039868b 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -114,6 +114,8 @@ Consequently, in addition to the allocation itself, it's very likely that the code generated for your function is far from optimal. Take such indications seriously and follow the advice below. +For more information about memory management and garbage collection in Julia, see [Memory Management and Garbage Collection](@ref man-memory-management). + 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 (the remaining allocation reported below is due to running the `@time` macro in global scope) and is significantly faster after the first call: From e875ca72af63a99f51988344a81556bb934a9718 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 16 Jun 2025 14:31:09 -0400 Subject: [PATCH 2/7] suggestions --- doc/make.jl | 2 +- doc/src/manual/memory-management.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/make.jl b/doc/make.jl index 02dd53984b079..15718091ece64 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -161,7 +161,6 @@ generate_markdown("NEWS") Manual = [ "manual/getting-started.md", "manual/installation.md", - "manual/memory-management.md", "manual/variables.md", "manual/integers-and-floating-point-numbers.md", "manual/mathematical-operations.md", @@ -193,6 +192,7 @@ Manual = [ "manual/code-loading.md", "manual/profile.md", "manual/stacktraces.md", + "manual/memory-management.md", "manual/performance-tips.md", "manual/workflow-tips.md", "manual/style-guide.md", diff --git a/doc/src/manual/memory-management.md b/doc/src/manual/memory-management.md index 30525c0b33f05..57a7bae58d926 100644 --- a/doc/src/manual/memory-management.md +++ b/doc/src/manual/memory-management.md @@ -35,8 +35,8 @@ If your system has limited or no swap space, you may experience out-of-memory er You can provide a hint to Julia about the maximum amount of memory to use: ```bash -julia --heap-size-hint=4G # Limit heap to ~4GB -julia --heap-size-hint=50% # Use up to 50% of physical memory +julia --heap-size-hint=4G # To set the hint to ~4GB +julia --heap-size-hint=50% # or to 50% of physical memory ``` The `--heap-size-hint` option tells the garbage collector to trigger collection more aggressively when approaching the specified limit. This is particularly useful in: From 27691716af8942a38463e916a02624f2807aff6f Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 17 Jun 2025 09:42:30 -0400 Subject: [PATCH 3/7] Fix mark threads note Co-authored-by: Andy Dienes <51664769+adienes@users.noreply.github.com> --- doc/src/manual/memory-management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/memory-management.md b/doc/src/manual/memory-management.md index 57a7bae58d926..e91ad3db81c9d 100644 --- a/doc/src/manual/memory-management.md +++ b/doc/src/manual/memory-management.md @@ -60,7 +60,7 @@ Julia's garbage collector can leverage multiple threads to improve performance o By default, Julia uses multiple threads for garbage collection: -* **Mark threads**: Used during the mark phase to trace object references (default: same as compute threads (thread 1 is shared)) +* **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) * **Sweep threads**: Used for concurrent sweeping of freed memory (default: 0, disabled) You can configure GC threading using: From d8a32149575efd556fa405dcc211010395ef1d7d Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 18 Jun 2025 10:31:19 -0400 Subject: [PATCH 4/7] fix docs on default gc thread count --- doc/src/manual/multi-threading.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/multi-threading.md b/doc/src/manual/multi-threading.md index 83852726f015b..8e8f2789ec3cb 100644 --- a/doc/src/manual/multi-threading.md +++ b/doc/src/manual/multi-threading.md @@ -84,8 +84,8 @@ julia> Threads.threadid() ### Multiple GC Threads -The Garbage Collector (GC) can use multiple threads. The amount used is either half the number -of compute worker threads or configured by either the `--gcthreads` command line argument or by using the +The Garbage Collector (GC) can use multiple threads. The amount used by default matches the compute +worker threads or can configured by either the `--gcthreads` command line argument or by using the [`JULIA_NUM_GC_THREADS`](@ref JULIA_NUM_GC_THREADS) environment variable. !!! compat "Julia 1.10" From 0b1e5e4b78479c75502c64f0e0e5943ed3005111 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 18 Jun 2025 10:32:09 -0400 Subject: [PATCH 5/7] fix small objects note --- doc/src/manual/memory-management.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/memory-management.md b/doc/src/manual/memory-management.md index e91ad3db81c9d..9f7744f7b88be 100644 --- a/doc/src/manual/memory-management.md +++ b/doc/src/manual/memory-management.md @@ -17,8 +17,8 @@ The garbage collector automatically reclaims memory used by objects that are no Julia uses a two-tier allocation strategy: -* **Small objects** (≤ 2032 bytes): Allocated using a fast per-thread pool allocator -* **Large objects** (> 2032 bytes): Allocated directly through the system's `malloc` +* **Small objects** (currently ≤ 2032 bytes but may change): Allocated using a fast per-thread pool allocator +* **Large objects** : Allocated directly through the system's `malloc` 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. From 76a607779ab5121bcf696238856df9fbeed6e69a Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sat, 12 Jul 2025 16:37:14 -0400 Subject: [PATCH 6/7] review suggestions Co-Authored-By: Gabriel Baraldi <28694980+gbaraldi@users.noreply.github.com> --- doc/src/manual/memory-management.md | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/doc/src/manual/memory-management.md b/doc/src/manual/memory-management.md index 9f7744f7b88be..2355fe699df58 100644 --- a/doc/src/manual/memory-management.md +++ b/doc/src/manual/memory-management.md @@ -100,13 +100,6 @@ julia> @time some_computation() 2.123456 seconds (1.50 M allocations: 58.725 MiB, 17.17% gc time) ``` -This shows: - -* Total execution time -* Number of allocations -* Memory allocated -* Percentage of time spent in garbage collection - ### GC Logging Enable detailed GC logging to understand collection patterns: @@ -144,10 +137,8 @@ The best way to minimize GC impact is to reduce unnecessary allocations: ### Memory-Efficient Patterns -* Use `Vector{Float64}` instead of `Vector{Any}` when possible * Avoid global variables that change type * Use `const` for global constants -* Consider memory pools for frequently allocated/freed objects ### Profiling Memory Usage @@ -155,13 +146,6 @@ For detailed guidance on profiling memory allocations and identifying performanc ## [Advanced Configuration](@id man-gc-advanced) -### Environment Variables - -Additional GC-related environment variables: - -* `JULIA_GC_WAIT_FOR_DEBUGGER`: Pause for debugger attachment during GC (debug builds only) -* `JULIA_PROFILE_PEEK_HEAP_SNAPSHOT`: Enable heap snapshot collection during profiling - ### Integration with System Memory Management Julia works best when: @@ -173,15 +157,6 @@ Julia works best when: ## [Troubleshooting Memory Issues](@id man-gc-troubleshooting) -### Out of Memory Errors - -If you encounter out-of-memory errors: - -1. **Check system memory**: Ensure adequate RAM and swap space -2. **Use heap size hints**: Set `--heap-size-hint` to limit memory usage -3. **Profile allocations**: Identify and reduce unnecessary allocations -4. **Consider chunking**: Process data in smaller chunks for large datasets - ### High GC Overhead If garbage collection is taking too much time: @@ -198,6 +173,5 @@ While Julia's GC prevents most memory leaks, issues can still occur: * **Global references**: Avoid holding references to large objects in global variables * **Closures**: Be careful with closures that capture large amounts of data * **C interop**: Ensure proper cleanup when interfacing with C libraries -* **Circular references**: While handled by the GC, they can delay collection For more detailed information about Julia's garbage collector internals, see the Garbage Collection section in the Developer Documentation. From 5952fa984b009e032cbb983357f99481a0bacdcd Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 13 Jul 2025 16:22:15 -0400 Subject: [PATCH 7/7] suggestion Co-Authored-By: Diogo Netto <61364108+d-netto@users.noreply.github.com> --- doc/src/manual/memory-management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/memory-management.md b/doc/src/manual/memory-management.md index 2355fe699df58..4efa683e3f249 100644 --- a/doc/src/manual/memory-management.md +++ b/doc/src/manual/memory-management.md @@ -9,7 +9,7 @@ Julia features a garbage collector with the following characteristics: * **Non-moving**: Objects are not relocated in memory during garbage collection * **Generational**: Younger objects are collected more frequently than older ones * **Parallel and partially concurrent**: The GC can use multiple threads and run concurrently with your program -* **Mostly precise**: The GC accurately identifies object references with minimal conservative scanning +* **Mostly precise**: The GC accurately identifies object references for pure Julia code, and it provides conservative scanning APIs for users calling Julia from C 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.