GCspy is a heap visualisation framework. It is designed to visualise
a wide variety of memory management systems, whether they are managed
by a garbage collector or implement explicit deallocation. Its target
users are mainly memory management implementers and it allows them to
observe the behaviour of their system. GCspy is not limited to small
toy systems; it can also visualise loads of realistic sizes, and do so
dynamically, as the system operates. However, if the user needs it, a
trace storing and replaying facility is also available.
What Can GCspy Visualise?
Even though it was designed for memory management, GCspy can in fact
visualise any system that is made up of a number of components (this
number should be low, say 1-5) and each component can be subdivided
into smaller partitions (these can range from a handful to thousands;
you are only limited by the screen size and memory of your
workstation). For example, in a generational memory management system,
each generation can be seen as a component and, for visualisation
purposes, can be subdivided into equal-size memory blocks. Alternatively,
using the same framework, GCspy can also visualise the free list
'component' of the memory system, assuming that a generation adopts an
in-place deallocation policy; in this case, each subdivision is a
single free list.
Architecture
A client-server architecture was adopted for GCspy; the system that is
being visualised is the server and the visualisation GUI is the
client; they communicate through standard sockets. This architecture
has several advantages: only the least possible amount of code is
added to the system being visualised, the client can be run on another
machine to affect the operation of the server as little as possible,
and the client can be connected to and disconnected from the server
when necessary. Additionally, only the server side of GCspy needs to
be adopted for a particular system. At connection-time, the server
sends bootstrap information to the client that describes its structure
and layout. Then, the client adjusts itself for that particular server
and starts accepting visualisation transmissions.
In order to visualise a system, the implementer has to write a GCspy
driver for each of its components. A driver is a module that
maps the operation and layout of a component onto the generic GCspy
abstractions: spaces, which represent components, and
streams, which represent a component's attributes. A driver is
customised for a particular system and component.
The above figure illustrates the architecture of GCspy, when
incorporated into a Java[tm] Virtual Machine. It is assumed that the
virtual machine has a generational memory system with two generations
and each generation is managed by a different garbage collector
(Semi-Spaces or S-S for the young generation and Mark&Compact or M&C
for the old generation). The illustration shows that, in order to
visualise this memory system, two GCspy drivers need to be implemented,
one per garbage collector.
The colors in the figure indicate the following:
Blue. The original ("vanilla") virtual machine.
Green. The generic GCspy framework that is re-used
whichever system GCspy is incorporated into. Notice that the whole of
the client is green.
Red. The only parts of the server that need to be
customised for a particular system. These include the two drivers and
a thin layer of code inside the virtual machine that collects the
state of the garbage collectors and communicates it to the drivers.
In the current implementation of GCspy, the client is written entirely
in Java using Swing, but there are three implementations of the server
infrastructure, in Java, C, and C++, for incorporation into systems
written in those languages.
The above screenshot shows GCspy visualising Sun's Java HotSpot[tm]
virtual machine running the SPECjvm98 _213_javac benchmark. Its memory
management adopts a generational framework with three distinct
components (or spaces in GCspy terminology): the young
generation (where most newly-allocated objects are allocated), the
old generation (where longer-lived objects are eventually
promoted to), and the permanent space (where "permament"
objects like classes, methods, etc. are allocated). Each space is
split into 16K blocks, each of which is represented by a tile
on the screen. The screenshot shows how full each space is (bright
red tiles represent memory blocks that are full of objects). The state
of the system reflected on the screenshot is at the end of a young
generation collection (this is indicated by the text field at the top
left of the window); this is why the young generation is relatively
empty. Finally, the large text area on the left of the window gives
detailed information about the selected tile (highlighted with a white
frame, roughly in the middle of the old generation).
The above screenshot was taken at exactly the same time as the
previous one, however the views have changed to represent the number
of objects per memory block (the brighter the tile is, the more
objects the corresponding block has). One thing we can immediately
notice is that the old generation seems "brighter" overall compared to
the permanent space; this is because the objects in the permanent
space tend to be larger, hence each block has a smaller number of
them. Additionally, in this screenshot, a tile in the young generation
is selected; notice that the information provided in the text area on
the left is different from that of the previous screenshot (GCspy
doesn't assume that all components will have the same attributes as,
in most cases, they don't).
History Graphs
The screenshots in the previous section show the state of the heap at
a single point in time. It is also extremely interesting to be able to
observe the behaviour of the visualised system over a period of
time. This is the purpose of the history graphs that GCspy can
generate. Each history graph is a grid of very small squares and
represents the behaviour of a single attribute of the component over
time. Each horizontal line of this grid represents a single
transmission (this usually corresponds to an event in the server) and
each vertical line represents the state change of a single component
subdivision (e.g. a memory block) over time. The color of each square
reflects the value of the attribute being visualised (e.g. bright when
the value is high). Every time a new transmission takes place, a new
horizontal line is appended at the end of the graph. According to
this, the y-axis essentially represents time.
The above history graph was taken from a single run of the SPECjvm98
_213_javac benchmark running on Sun's Java HotSpot virtual machine. It
shows how full the old generation is over time. The horizontal white
lines represent old generation collections. The red area in the graph
is the used space in the generation (and remember: time moves
downwards). Notice how the generation fills up, as time passes by, and
then empties immediately after each old generation collection.
This second history graph was taken at the same time as the first one,
however it shows which areas of the old generation contain references
that point to the young generation (a light gray color indicates that
there are no such references; a black color indicates that there are a
few such references; a bright color indicates that there are many such
references). First notice that the colors are mainly gray and black;
this is because no memory block contains more than a few references to
young objects. Additionally, notice that black squares are scattered
around the graph and there seems to be a large number of
them. Usually, generational memory systems assume a small number of
old-to-young references; this is not the case here. The last
observation that can be made from this graph, when comparing it to the
previous one, is that objects newly-promoted in the generation
(e.g. the ones that are on the edge of the red area in the previous
graph), seem to have references to young objects; this is indicated by
the black line in the bottom graph that matches the edge of the red
area in the top graph.
GCspy-Related Publications
T. Printezis and A. Garthwaite. Visualising the Train Garbage
Collector. In D. Detlefs, editor, Proceedings of the 2002
International Symposium on Memory Management, pages 50-63, Berlin,
Germany, July 2002.
pspdf
T. Printezis and R. E. Jones. GCspy: An Adaptable Heap
Visualisation Framework. In S. Matsuoka, editor, Proceedings of the
17th Annual ACM Conference on Object-Oriented Programming, Systems,
Languages, and Applications (OOPSLA 2002), Seattle, WA, November
2002.
pspdf