The .NET garbage collector can be your best friend or your worst enemy; and it’s not friendly with a lot of people. The GC left more than a few production systems burning in smoke after developers failed to anticipate the effects of real production loads on the memory subsystem. In this talk, we will methodically measure and improve the .NET garbage collector’s performance. We will begin with a quick refresher on dynamic performance tools that can identify GC issues: CLR performance counters, ETW GC events, and ETW object allocation events; as well as static analysis tools, such as the Roslyn-based heap allocations analyzer. Then, we will inspect multiple issues at the source code level: excessive boxing, unintended effects of lambdas closing over local variables, await-generated state machines, intermediate objects in LINQ queries, and many others. We will also discuss higher-level memory problems: how to get rid of large object allocations, how to avoid finalization, and how to convert heap-based designs to local objects. Some of these ideas are now being applied at the language and framework level in C# 7 and .NET Core. At the end of the talk, you will be equipped to reduce memory traffic and GC overhead in your own applications, often by a factor of 10 or more!
2. SDP MAY 2017 #SELASDP | @DINAGOZIL
AGENDA
▸ Motivation
▸ Identifying memory traffic problems
▸ Specific code patterns
▸ General tips and tricks
2
3. SDP MAY 2017 #SELASDP | @DINAGOZIL
IT’S NOT THAT SIMPLE
▸ Memory allocations are cheap
▸ But not free
▸ And have side effects
▸ Don’t forget to think about the memory
3
4. SDP MAY 2017 #SELASDP | @DINAGOZIL
THIS ISN’T JUST THEORY
4
5. SDP MAY 2017 #SELASDP | @DINAGOZIL
THIS ISN’T JUST THEORY
5
7. SDP MAY 2017 #SELASDP | @DINAGOZIL
PERFORMANCE COUNTERS
▸ Provide numeric performance information about the system
▸ Located in different areas in the system - disk, .NET, networking, OS objects
▸ Available on-demand using built-in tools like perfmon and typeperf
▸ Can write your own but that’s not the topic of our talk…
7
9. SDP MAY 2017 #SELASDP | @DINAGOZIL
DIVE DEEPER WITH EVENT TRACING FOR WINDOWS
▸ High-speed logging framework supporting more than 100K structured
messages per second
▸ .NET, drivers, services, third party components
▸ Can be turned on on-demand while running
▸ Very small overhead
▸ Can write your own but that’s not the topic of our talk…
9
11. SDP MAY 2017 #SELASDP | @DINAGOZIL
STATIC CODE ANALYSIS
▸ Can detect code issues while coding without running the application
▸ Usually based on pattern-detection
▸ Undisposed IDisposable-s
▸ Inefficient String concatenations
▸ Wrong number of “dynamic” parameters
▸ ReSharper, Visual Studio, and more
11
12. SDP MAY 2017 #SELASDP | @DINAGOZIL
TIPS IN VS
12
13. SDP MAY 2017 #SELASDP | @DINAGOZIL
DYNAMIC MEMORY PROFILING
▸ Dynamic analysis can help uncover more subtle scenarios
▸ Can collect temporal information
▸ Managed and unmanaged memory leaks
▸ Lots of commercial tools available: .NET Memory Profiler, ANTS, dotMemory…
13
14. SDP MAY 2017 #SELASDP | @DINAGOZIL
.NET MEMORY PROFILER AND MEMORY LEAKS
14
15. SDP MAY 2017 #SELASDP | @DINAGOZIL
WHO’S HOLDING MY OBJECTS?
15
18. SDP MAY 2017 #SELASDP | @DINAGOZIL
STRUCTS, COMPARISONS AND WHAT’S BETWEEN THEM
▸ We have a data structure containing a single int value
▸ How long does it take to search an element in a list of such data structures?
▸ Different implementations
▸ Default comparison
▸ Override Equals
▸ Implement IEquatable
18
40. SDP MAY 2017 #SELASDP | @DINAGOZIL
DON’T CALL GC.COLLECT()
▸ You have a large heap. Don’t make it worse by frequent calls to full GC which is
going to take a long time.
▸ Instead, use what we learned to reduce memory usage
▸ And don’t forget to remove debugging code from production
40
41. SDP MAY 2017 #SELASDP | @DINAGOZIL
THIS IS NOT A DRILL
41
42. SDP MAY 2017 #SELASDP | @DINAGOZIL
AN (UN)FORTUNATE COINCIDENCE
42
43. SDP MAY 2017 #SELASDP | @DINAGOZIL
PREFER VALUE TYPES
▸ Value types (structs) have a compact memory layout, and can be embedded in
their parent object, making cache’s life easier and generally reducing footprint
43
44. SDP MAY 2017 #SELASDP | @DINAGOZIL
BASED ON A REAL STORY
44
45. SDP MAY 2017 #SELASDP | @DINAGOZIL
POOL, BUFFER AND REUSE EXPENSIVE OBJECTS
▸ Pool expensive or large objects
instead of returning them to GC
▸ Many OSS pools out there
▸ For large primitive buffer (e.g.
byte[]) may use slices
▸ A large preallocated buffer which
is sliced into small parts
▸ System.Slices on GitHub
45
46. SDP MAY 2017 #SELASDP | @DINAGOZIL
HARD TO WATCH
46
47. SDP MAY 2017 #SELASDP | @DINAGOZIL
AVOID FINALIZATION
▸ Finalizers run at some point after the object is no longer referenced by the
application (non-deterministic)
▸ Finalizers run on a separate thread and create potential concurrency issues
▸ Finalization prolongs object lifetime and can create leaks if finalizers don’t
complete quickly enough
▸ Better to use deterministic resource management (IDisposable)
47
48. SDP MAY 2017 #SELASDP | @DINAGOZIL
SUMMARY
▸ Although a single memory allocation is extremely fast, it adds up
▸ All based on real questions, stories and bugs
▸ Don’t overcomplicate where it’s not needed
▸ Measure
▸ And optimize…
▸ DIY: https://github.com/dinazil/look-mommy-no-gc
48