ObjectLayoutInspector and the hidden shape of a CLR type
A tool that helps to see an internal structure of the CLR types at runtime
At a glance
- What is it?
- ObjectLayoutInspector prints the in-memory field layout of a CLR type at runtime, which the runtime itself refuses to document. It reaches offsets through generated IL rather than the profiling API, targets x64 only, and its sample output shows exactly how much padding a struct is paying for.
- Who is it for?
- ObjectLayoutInspector earns a place in any team that profiles allocations or worries about struct padding, since it shows the layout the CLR never promised to document and it needs no debugger attached. Skip it for x86 targets, where the results are explicitly unsupported, and skip it if you need the size of a type rather than the offsets of its fields, because the size computation is unfinished in the current source.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why the runtime does not tell you the layout
There is no official documentation for field layout in the CLR, and the reason given in the project README is deliberate on the part of the runtime authors: they reserved the right to change it. That freedom is exactly what makes the question hard to answer. A developer who wants to know where a field actually sits in memory normally reaches for raw memory inspection in Visual Studio or the !dumpobj command in the SOS debugging extension, and the README calls both approaches tedious.
ObjectLayoutInspector exists to replace that ritual with one call. It prints a type layout to the console or hands back a layout object you can consume programmatically. The target reader is someone working on a performance critical application, or anyone who is simply curious about how the runtime arranged their fields.
The output is a diagram, not a table. Here is the layout the README shows for a struct called NotAlignedStruct:
Type layout for 'NotAlignedStruct'
Size: 12 bytes. Paddings: 4 bytes (%33 of empty space)
|================================|
| 0: Byte m_byte1 (1 byte) |
|--------------------------------|
| 1-3: padding (3 bytes) |
|--------------------------------|
| 4-7: Int32 m_int (4 bytes) |
|--------------------------------|
| 8: Byte m_byte2 (1 byte) |
|--------------------------------|
| 9: padding (1 byte) |
|--------------------------------|
| 10-11: Int16 m_short (2 bytes) |
|================================|That report quantifies the thing struct authors usually guess at: a third of the instance is padding.
LdFlda and a Dynamic Method instead of the profiling API
The mechanism is the interesting part. The project deliberately avoids unmanaged code and the Profiling API, and instead leans on the LdFlda IL instruction, which returns the address of a field for a given type. That instruction is not exposed in the C# language, so the library does light-weight code generation to get at it.
A Dynamic Method named GetFieldOffsets is emitted with skipVisibility set to true, tied to the module of the helper class. The generated body allocates an array of long, then for each field it pushes the array and the index, pushes the object instance, emits Ldflda for that field, converts the address with Conv_I8, and stores it into the array. The delegate is then created and cast to Func<object, long[]>.
Offsets are derived by taking the smallest returned address as a baseline and subtracting it from every other address, then ordering the results by offset. That baseline trick has a visible consequence: a field with no storage of its own can land at offset zero, which is why the sample output shows the Int32 field first even though it is declared second.
The instance used for measurement is created by a helper that picks between two strategies:
private static object CreateInstance(Type t)
{
return t.IsValueType ? Activator.CreateInstance(t) : FormatterServices.GetUninitializedObject(t);
}Value types go through Activator.CreateInstance, while reference types without a default constructor are handled by FormatterServices.GetUninitializedObject, the lesser known generic factory that skips the constructor entirely.
Installing the package and printing your first layout
Installation is a single NuGet reference, taken from the README:
dotnet add package ObjectLayoutInspectorThere is no separate tool to install and no service to start. The package is a library you reference from the process whose types you want to inspect, because the layout is only knowable from inside a running runtime.
The first thing to do is print a layout to the console:
using ObjectLayoutInspector;
// Print the layout to the console
TypeLayout.PrintLayout<YourType>();
// Or get the layout programmatically
TypeLayout layout = TypeLayout.GetLayout<YourType>();
Console.WriteLine(layout);The generic and programmatic forms are interchangeable in practice. The first is for a quick look during development; the second gives you an object you can hold, compare or dump into your own diagnostics.
Working at the field level requires a little more care, because the offsets are what the layout diagram is built on. The README demonstrates that with a class holding a byte and an int:
Console.WriteLine(
string.Join("\r\n",
InspectorHelper.GetFieldOffsets(typeof(ByteAndInt))
.Select(tpl => $"Field {tpl.fieldInfo.Name}: starts at offset {tpl.offset}"))
);The output the README gives for that pair of fields is:
Field n: starts at offset 0
Field b: starts at offset 4The layout is not the whole story: size is still open
Offsets alone leave a gap, and the README is direct about it. Knowing where each field starts does not tell you how large the instance is, and without that number you cannot judge how much of the object is wasted space. The document states plainly that it is interesting, but not sufficient.
The section that would address this is titled Computing the size for a type instance, and it opens by repeating that there is no official way to get it. The source excerpt ends there, so the size story stops mid-sentence in the version of the README that is published.
This matters for how you read the diagram. The NotAlignedStruct sample reports a size of 12 bytes with 4 bytes of padding, so the size path does work for the printed output. What is not documented is the general path for computing the size of an arbitrary type instance at runtime, and the README does not explain how that size is derived. Treat the reported size as something the tool already calculates for the display, not as a general purpose API you can rely on.
The same truncation applies to the closing sections. If you need per-field sizes rather than offsets, or a serialization compatible view of the layout, this README gives you no path to either.
x64 only, and the baseline that hides a field
Two constraints are worth reading twice. The first is stated in a note at the top of the README: all layout examples and sizes assume an x64 runtime, and the library currently targets x64 only, with results on x86 not supported. Any process that can still run 32-bit, which includes some legacy .NET Framework services, is outside the tool's supported range.
The second constraint is a design consequence rather than a stated limitation. The offset calculation subtracts the smallest address from every address to produce a baseline. For a struct whose first field is a byte and second field is an int, the byte is placed at offset 0 and the int at offset 4, but the sample output in the README lists the int field first at offset 0 and the byte at offset 4. The ordering by offset and the choice of baseline together can therefore present fields in an order that does not match the declaration order, and a field that shares a position with another can be indistinguishable by offset alone.
For a reflection based library this is a reasonable trade: it avoids unmanaged code and avoids depending on the profiling API, at the cost of a reading of the output that needs care. The tool tells you where bytes sit, and you still have to map that back to the type you declared.
Maintenance: one 2018 release and a 2026 commit
The repository is not archived, and the last push was on 2026-04-22. That is well after the only published NuGet release, which is 0.1.1.0 marked as the initial release and dated 2018-04-23. The gap between the last release and the last commit is the thing to plan around: work has continued in the repository without a corresponding package version.
The license is MIT, which is short enough to read in a minute and permissive for a library that lives inside your diagnostics code. There is no third party dependency mentioned in the README, and the implementation stays inside managed IL generation, so the adoption surface is small.
Upgrade cost is mostly a question of whether the NuGet package you reference matches the source you have read. If you rely on TypeLayout.PrintLayout and InspectorHelper.GetFieldOffsets, the API names in the README have been stable enough to have stayed in the README for years. Pin the package version deliberately and read the diff against the version you currently use, since a repository that commits without cutting releases can leave you uncertain about which behaviour you have.
Editorial conclusion
ObjectLayoutInspector earns a place in any team that profiles allocations or worries about struct padding, since it shows the layout the CLR never promised to document and it needs no debugger attached. Skip it for x86 targets, where the results are explicitly unsupported, and skip it if you need the size of a type rather than the offsets of its fields, because the size computation is unfinished in the current source. Before adopting it, run the layout print on a couple of your own hot structs to see the padding share, and pin the NuGet version deliberately, since the only published release is 0.1.1.0 from 2018 while the repository was last pushed on 2026-04-22.
Frequently asked questions
How do I install ObjectLayoutInspector?
Add the NuGet package to your project with the command dotnet add package ObjectLayoutInspector. There is no separate executable to install, since the tool inspects types inside the running process.
Does ObjectLayoutInspector work on 32-bit runtimes?
No. The README states that all layout examples and sizes assume x64, and that the library currently targets x64 only, so results on x86 are not supported.
How does ObjectLayoutInspector find field offsets without a debugger?
It generates a Dynamic Method that emits the LdFlda IL instruction for each field, then converts the returned addresses into offsets relative to the smallest address. It deliberately avoids unmanaged code and the Profiling API.
Can ObjectLayoutInspector tell me the size of a type instance?
The printed layout reports a size and a padding share, as the NotAlignedStruct example shows with 12 bytes and 4 bytes of padding. The README section on computing the size for a type instance is cut off mid-sentence, so the general path to a size at runtime is not documented.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sergeyteplyakov-objectlayoutinspector)Was this useful?