Compact Framework Memory Management Chris Tacke OpenNETCF Consulting

Download Report

Transcript Compact Framework Memory Management Chris Tacke OpenNETCF Consulting

Compact Framework Memory
Management
Chris Tacke
OpenNETCF Consulting
www.OpenNETCF.com
[email protected]
Introduction
• Microsoft Windows CE memory architecture
• Windows CE application memory usage
• GC heap
●
How it grows and shrinks
• JIT Heap
●
How it grows and shrinks
• Demo - Remote Performance Monitor (RPM)
• Managed application costs
• Managed code and deterministic behavior
RAM DLLs load at the
top of the slot
Code loads at
the bottom
App data and resources
load above code
Stack and heaps load
in reserved space
above resources
Remainder is free virtual
space for process
In-ROM DLLs are
in Slot1 (XIP)
Other processes
are paged in
Shared
memory
RAM DLLs
Virtual Space
Slot
Slot 0
0
0x8000 0000
Other
Slots 2-31
processes
Stack/heap
Data
Code
0x0400 0000
ROM
SlotDLLs
1
0x0200 0000
Slot 0
0x0000 0000
Executing a Managed Application
The typical managed developer’s view
My application
Application
Loads some references
Managed
references
Uses the Microsoft .NET
Compact Framework
Calls some arcane “Native stuff”
Compact
framework
Native
stuff
A Deeper Look
1. CLR loads
2. App and
class library
assemblies load
3. EE JIT
compiles code
4. Reference types
allocated from
GC Heap
5. Types generated
in memory
from metadata
6. Miscellaneous
allocations
Application
Shared
memory
MyApp.exe
Reference
JITted
code
Types
CLR
Managed
references
MyReference.dll
Miscellaneous
mscorlib.dll
mscoree.dll
Compact
framework
Metadata
0x8000 0000
mscoree2_0.dll
System.dll
Slots 2-31
System.Data.dll
netcfagl2_0.dll
Native
stuff
0x0400 0000
CLR 1
Slot
0x0200 0000
0x0000 0000
JITted
Slotcode
0
GC Heap
AppDomain Heap
GC heap holds allocations for
reference types in 64K segments
Allocations in a segment are fast
because they are simple pointer
arithmetic
Object H
Object G
64+K Segment
Object F
Eventually an allocation will require
more memory than the heap contains
Object E
If more memory is not available,
OOM Exception is thrown
Object E
Another segment is added to the
Heap through a VirtualAlloc call and
allocation continues.
NOTE: Segments are not
necessarily contiguous memory.
If an object requires > 64K allocation,
a segment large enough is created for
the allocation
(not necessarily a 64K multiple)
Object D
64+K Segment
ptr
Object C
Object B
Object A
GC Resource Recovery
• Resources are recovered through a process
called “collection”
• A collection is triggered by specific conditions
●
●
●
●
●
For every 1 MB of heap data allocated
(not tied directly to heap size)
An OOM on most large allocations (bitmaps, etc.)
The application receives a WM_HIBERNATE
message
The application moves to the background
The application explicitly calls GC.Collect
GC Collection Activities
• A collection will always:
●
Free unreferenced objects in the heap that have no
finalizer or whose finalizer has already run
• A Collection may:
●
●
●
●
Compact the GC heap (combine small free spaces by
moving objects)
Shrink the GC heap itself
Release (pitch) JITted code
The application explicitly calls GC.Collect
• To fully understand GC resource recovery, we
need a deeper understanding of allocation
GC Internals
Strong references to objects are called
“roots” and are maintained
by the CLR
When new objects are created, a
reference is stored as a root
If an object has a Finalize method, a
reference to it is also added to the
finalization queue
As objects are destroyed or go
out of scope, roots are removed
During collection the GC walks all
roots, marking all actively referenced
objects in the heap (mark)
After marking all objects from
roots, unmarked objects are
released (sweep)
Finalization
queue
Object E
Roots
Object E
(Has Finalize)
Strong
references
Globals
Object D
(No Finalize)
Statics
Locals
Object C
Object B
Object A
GC Internals
When a finalizable object is destroyed
root reference is removed as usual
During GC Collection objects in
finalization queue are not released
Instead, a reference is added to a
special root called the freachable
queue and the finalization queue
entry is removed
Finalization
queue
Object E
Finalizer
thread
Roots
Freachable
queue
Object E
(Has finalize)
Once collection is complete, a
separate thread runs Finalize on all
items in the freachable queue
Once Finalize has been run, the
freachable reference is removed
Object C
On the next collection the object
memory will be released
Object A
After Collection
ptr
After a collection cycle, allocations
begin again from the beginning of the
heap. An allocation is made in the first
“hole” into which it will fit
Object H
Allocation pointer moves to the
beginning of the first segment
Segments hold a pointer to first
available hole so locating it
is still fast
Allocator will attempt to fill holes with
subsequent allocations. The pointer will
continue to move up until either a large
enough hole is found or we reach the
top of the GC heap and new memory
must be allocated
Same process happens with “active”
segement before new segments
are created
ptr
Object X
ptr
Object C
ptr
ptr
Object A
GC Heap Compaction
When the GC heap has 750k or more of
free space (holes) during collection, a
compaction occurs
Object H
Note that the GC heap does not
necessary shrink when this occurs
If the GC heap exceeds 1MB in size,
segments will be released down to
the 1MB line if they are empty
0x0010 0000
Object H
Object C
Object C
0x0000 0000
Object A
JIT Compiler
• Has its own heap separate from the GC heap
• Does not highly optimize during compiling
●
●
Speed of compile chosen over code density or
execution speed
Roughly 50% as “good” as the FFx compiler
or a typical native C++ compiler
• Optimized for short code paths
●
●
Method calls are 2-5 times slower than in the FFx
or native code
Avoid recursion and heavy use of abstraction layers
JIT Heap Growth (JIT Compiling)
• IL is compiled per method and on the fly
●
●
Small increments
Assembly is 2-3 times the size of the source IL
• JIT Heap growth is unbounded if .Net
Compact Framework 2.0
JIT Heap Shrinkage (Code Pitching)
• Shrinks by almost all code
●
●
Unlike growth, shrinking is not incremental
Compiler keeps only JITted code for current
call stack
• Triggers
●
●
●
Memory allocation failure
WM_HIBERNATE message received
Application is sent to the background
The Fixed Costs of Managed Code
• .NET CF 2.0 takes ~2.2 MB in ROM
(compressed)
• CLR native components require 650K virtual
space, max of 650K physical
●
Slot 1 if in ROM, Slot 0 if installed in the field
• .NET CF-managed assemblies take 3.8 MB of
shared virtual space plus 1-2 MB of physical
space from the 32 MB process slot
●
This is a one-time cost per device (shared across all
managed applications)
The Fixed Costs of Managed Code
• Applications allocate out of the 1 GB shared
slot as memory-mapped files
● Applications are compressed and will grow in
virtual size ~50% when mapped
● Required physical memory is demand paged
in (probably ~50% of virtual size required to
prevent thrashing)
The Dynamic Costs of Managed Code
• CLR data structures require <250K
• JIT Heap typically 250K-500K from the 32 MB
process space
●
●
Long code paths create requirements to hold more
JITted code at one time. Even without the perf hit for
method calls this can be a problem
Almost all JITted code is pitched when app is
in the background
• GC heap size is unbounded
●
Comes from the 32 MB process space
The Dynamic Costs of Managed Code
• Each thread gets a 64K stack
●
●
Allocated from 32 MB process space
Not freed until the thread is collected
“Controlling” the GC
• Rule 1: The GC cannot be directly
controlled except by calling GC.Collect
• When you call GC.Collect the GC must:
1. Suspend
all threads in the process
2. Traverse all roots
3. Mark all objects with a referring root
4. Traverse all objects in the GC heap to see
if they’re marked
5. Traverse the finalizer queue
“Controlling” the GC
• Any GC.Collect() call is inherently expensive and
it’s extremely rare that an application should
ever call it
• Since finalizers are run on a separate thread, it
is not guaranteed, and is actually unlikely, that
Finalize method execution will be done when the
call to GC.Collect returns
●
●
Deterministic finalization is not possible
Never assume finalization if you manually call collect
“Real Time” and Determinism
The inherent problem with managed environments
• What exactly is “real time”?
●
“Real time” is defined as a system with
deterministic results. This means that you can
guarantee that when an action occurs, the
time for the reaction has an defined upper
bound.
• Example: An interrupt occurs, the handler
must run within some bounded time frame
or bad things happen (system failure,
damaged product, injury, etc)
“Real Time” and Determinism
The inherent problem with managed environments
• By their very nature, all garbage-collected
systems (FFx, .NET CF, Java, etc.) are
inherently nondeterministic
●
●
●
GC can run at any time
Time required to collect is indeterminate
GC suspends all other threads during collection
• Reminder: Since finalizers are run on a
separate thread it is not guaranteed, and is
actually unlikely, that finalizers will be done when
GC.Collect returns. This means deterministic
finalization is not possible
Determinism in the
Managed Environment
Achieving the impossible
Requires exhaustive testing and a thorough
understanding of the entire system, not just your
application. Not recommended for critical or nonfault tolerant systems.
Determinism in the
Managed Environment
Achieving the impossible
• To get deterministic behavior we must:
●
Identify what parts of our code need
determinism
• Determinism of entire app is not achievable
●
●
Protect those parts from the effects of GC
Collection
Remember my Three Rules of Managed
Determinism
The Three Rules of
Managed Determinism
1. Collection occurs when certain known
trigger events occur (i.e. OOM, 1 MB
allocation, etc.)
2. Collection runs on the current thread,
not its own thread
3. Thread behavior is “adjustable” at the
API level
Determinism in the
Managed Environment
Step 1: Handle GC triggers
• Allocation-based triggers (e.g. OOM or
1MB GC heap threshold)
●
●
Never, ever (ever) make an allocation in
your real time code
Beware of boxing (allocations made on your
behalf)
Determinism in the
Managed Environment
Step 1: Handle GC triggers
• WM_HIBERNATE event
●
It’s your system, do your best to know what
else is running
• Open systems are far more fragile than closed
systems
●
Intercept WM_HIBERNATE with your own
message pump (e.g. IMessageFilter
implementation)
Determinism in the
Managed Environment
Step 1: Handle GC triggers
• Application moving to the background
●
Disallow it during execution of real time
code sections
• Use Mutexes, Critical sections, etc
Determinism in the
Managed Environment
Step 1: Handle GC triggers
• Direct calls to GC.Collect
●
Even if you’re tempted, just don’t do it
Determinism in the
Managed Environment
Step 2: Protect yourself
• Take advantage of Rules 1 and 2. Since GC
runs on the current thread and thread behavior
is API adjustable.
●
Put your real-time code in a separate thread
• Assuming we’ve handled step 1 and make no allocations, GC
will always run in the non-real time thread
●
●
●
Raise your real-time thread’s priority (above normal
application priorities) via CeSetThreadPriority
Set your real-time thread’s quantum to “run to
completion” via CeSetThreadQuantum
Keep your real-time thread to a minimum to
avoid system impact
Sample Real Time IST
private void LaunchStartup()
{
Thread2 ist = new Thread2(ISTProc);
// set IST priority well above app priority
ist.RealTimePriority = 200;
// run to completion
ist.RealTimeQuantum = 0;
ist.Start();
}
Sample Real Time IST
private void ISTProc()
{
EventWaitHandle interruptWaitHandle =
new EventWaitHandle(
false,
EventResetMode.AutoReset,
"ISR_EVENT_NAME");
// allocate any variables here, outside the IST loop
while(true)
{
interruptWaitHandle.WaitOne();
// handle the interrupt here
// do *NOT* allocate any variables
}
}
Questions?
• Newsgroups
●
microsoft.public.dotnet.framework.compactframework
• Blogs
●
●
http://blog.opennetcf.org
http://blogs.msdn.com/netcfteam
• Slide deck is on CommNet
• Code will be posted in my blog
Chris Tacke
OpenNETCF Consulting
www.OpenNETCF.com
[email protected]