Performance: Excessive file I/O - Changelog read 18x/sec, uncached OAuth, subprocess overhead
Summary
Profiling Claude Code with strace reveals several I/O inefficiencies that contribute to high CPU/memory usage.
Background
While investigating OOM issues with Claude Code (observed 1.6GB memory usage for a single process), I ran strace to understand the I/O patterns. At the time, Claude Code was performing git operations - specifically checking out individual files from a previous working commit and comparing them against the current HEAD state on a private repository.
Disclaimer
This 38-second strace capture is a limited sample and may not provide complete insight into all I/O or memory issues. However, we hope the patterns identified here are indicative and helpful for future optimization efforts.
Environment
- Claude Code version: 2.1.x (latest)
- Platform: WSL2 / Linux (kernel 6.6.87.2-microsoft-standard-WSL2)
- Profiling command:
sudo strace -f -tt -p 52212 -e trace=read,open,stat > claude_strace.txt 2>&1 - Duration: ~38 seconds of normal git-related operation
Findings
1. CHANGELOG.md read 18x/second (5.5 MB in 38s)
The changelog file is being read repeatedly at ~8KB per read, totaling 683 reads and 5.5 MB in 38 seconds. This appears to happen on every operation cycle.
Changelog_md: 683 reads, 5,450,686 bytes total
🔴 WASTEFUL: Reading entire changelog ~8KB per read!
Suggestion: Cache changelog content in memory or read once at startup.
2. OAuth credentials read from disk repeatedly (1.7x/sec)
Credentials file is re-read from ~/.claude/.credentials.json ~64 times in 38 seconds instead of being cached in memory.
OAuth_credentials: 64 reads, 27,712 bytes total (433 bytes each)
Suggestion: Cache credentials in memory, only re-read on auth failure or file change (inotify).
3. Excessive subprocess spawning (83 in 38s)
Each subprocess (mostly git commands) incurs:
- ELF binary loads (74 reads, 62KB)
- Locale alias loading (35 reads, 105KB)
- Shell profile/bashrc reads (26 reads, 56KB)
- Git config reads
⚠️ Subprocess overhead:
83 subprocesses spawned in 37.7s
74 ELF binary loads, 35 locale file reads
Suggestion: Batch git operations, use git plumbing commands that can return multiple pieces of info, or consider a long-running git helper process.
4. Unbuffered single-byte pipe reads (573 reads)
Reading from pipes one byte at a time instead of buffered reads.
single_char_unbuffered: 573 reads, 573 bytes total
🔴 587 single-byte reads! Use buffered I/O
Suggestion: Use buffered I/O for pipe reading.
Full Profile Output
Time range: 38 seconds of git-related operation
Total read() calls: 2,146
Total bytes read: 5.68 MB
Read frequency: 57.0 reads/sec
BY CONTENT TYPE (sorted by read count)
Type Count Bytes
Changelog_md 683 5,450,686
single_char_unbuffered 573 573
empty/EOF 385 0
ELF_binary 74 61,568
OAuth_credentials 64 27,712
binary_inotify_epoll 48 496
git_HEAD 42 966
locale_alias 35 104,860
gitconfig_user 29 2,349
git_config 28 8,789
shell_config 26 55,984
settings_json 24 155,838
TLS_traffic 13 31,108
TLS_handshake 6 7,232
Profiler Script
We wrote a Python script to parse and categorize strace output - happy to share if useful for internal profiling.
Impact
These inefficiencies compound over time, especially in long sessions, and likely contribute to the high memory usage (1.6GB observed for a single Claude Code process) and general system load.
---
<sub>© 2026. This issue content is provided to Anthropic for bug reporting purposes only. Reproduction or use in derivative content (videos, articles, social media) without explicit written permission is prohibited.</sub>
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗