|
With the restrictions removed, we have
the opportunity to perform real-time analysis. Now it will be
possible to monitor your game’s memory behaviour in real-time. We
can even record the history of the memory mutations. This makes it
possible to spot something I like to call ‘logical memory leaks’.
This term will be explained later on in
the article during the discussion of the recording view. And with
memory restrictions removed, we can send over the entire callstack of
an allocation – I was often forced to limit callstack information
due to memory constraints. But be warned that the tool needs a good
strategy to cope with all this data, especially during recording, as
we are sending massive information over the network. To show what
amount of data we are talking about, another test run is performed.
Just starting up the main menu in our
game shows that already over 4 million packages have been sent
- a package is any allocation, reallocation or free of a memory
block. On average these packages have a size of 93 bytes. This means
that when entering the main menu, almost 400 megabytes have already
been sent to the tool.
The MemAnalyze
features
This part will demonstrate the current
features of MemAnalyze and how they help to solve memory problems.
Monitoring the current state
When the tool is started and a
connection has been established, the tool will always simply track
the current state. Figure 1 shows that there are some global
statistics already in view. At any moment in time, we can click a
button to perform any of the following forms of analysis:
Figure 1: The main screen. On top
some global memory statistics are displayed. To the right two memory
state ‘slots’ are used to compare memory states. The remainder of
the dialog is filled with a list to manage memory snapshots.
1) CallGraph analysis. This view
displays the CallGraph. It resembles some of the ideas from the
hierarchy view that was discussed in the previous articles.
For each function, the amount of allocations, the total size and the
percentage with respect to the game’s total allocation size are
displayed. It is very easy to see the critical path in your memory
consumption here. If you click anywhere in the CallGraph, source code
with file and line information is displayed (see figure 2).
Figure 2: The CallGraph view.
2) TopX analysis. I named this
one after the TopX view of the Xbox profiler. It displays all
functions that directly or indirectly allocated memory along with
their names, the allocation count, the total allocation size and the
percentage relative to the game’s total allocation size. In this
view you can sort on any of these types. When a function is clicked,
source code is displayed (see figure 3).
Figure 3: The TopX view.
3) Fragmentation analysis. This
view displays the physical/virtual memory blocks as they are in
memory. By clicking on any block, the callstack for that block is
displayed. As for all views, source code is displayed when a function
is clicked (see figure 4).
Figure 4: The Physical layout view.
4) Allocation overview. This
view displays a graph of all of the allocations sorted either by size
or by count. The horizontal axis displays the amount of bytes and the
vertical axis displays either the allocation count or the total
allocation size (see figure 5). This information can be very helpful
if you want to write a custom memory manager. Custom memory managers
can be written to optimise for a certain allocation size. For
instance, have a look at the Boost memory pool implementation1, or
have a look at how Windows XP and Windows Server 2003 use a Low
Fragmentation Heap to avoid memory fragmentation2.
Figure 5: The allocation overview.
|