A 40 GB file that only occupies 6 GB of actual disk space is not a rounding error or a display bug — it is a sparse file, and macOS uses them constantly, especially around virtual machines and disk images. A sparse file has "holes": regions the file logically contains but which were never actually written, so the filesystem doesn't bother allocating real disk blocks for them. The file reports a size that reflects everything it could contain, while the space it truly uses on disk reflects only what has been written so far. Understanding this distinction explains why Finder, ls, and du can disagree about the same file, and why some of the largest-looking files on your Mac barely dent your free space.

What a "hole" in a file actually is

Filesystems store files as a set of allocated blocks referenced by the file's inode. Normally, if a file is 100 MB, the filesystem has allocated roughly 100 MB of physical blocks to hold it. A sparse file breaks that assumption: parts of the file's address range have no blocks allocated at all. When a program reads from one of these unallocated regions, the filesystem doesn't retrieve garbage from disk — it simply returns zeroes, as if the block were there and full of nulls. Writing real data into a hole causes the filesystem to allocate a block for that specific region at that point, and only then does it start consuming actual space.

This means a program can create a huge file — reserving space for a database, a disk image, or a virtual machine's entire virtual disk — without the filesystem needing to physically write gigabytes of zeroes up front. The file behaves, from the application's perspective, exactly as if it were fully allocated.

Logical size versus allocated size

This gives every sparse file two different, both entirely honest, numbers:

  • Logical (nominal) size — the size the file reports through its metadata: how large the file appears to any program that reads its length, including Finder's Get Info and the plain ls -l output.
  • Allocated (on-disk) size — the number of disk blocks actually backing the file, which is the number that matters for how much free space you actually have.

For an ordinary, fully written file these two numbers are the same. For a sparse file, the allocated size can be dramatically smaller than the logical size, because large stretches of the file are holes rather than real data.

How ls -ls and du diverge from the nominal size

The default ls -l prints the logical size — the number that comes from the file's length as recorded in its metadata, regardless of whether the underlying blocks exist. To see the block-based, on-disk figure instead, use ls -ls, which prefixes the usual listing with a block count reflecting real allocation:

ls -ls somedisk.dmg

The leading number is in disk blocks, not bytes, but a large gap between that figure (converted to bytes) and the size shown later in the same line tells you the file is sparse. You can get a size in more familiar units for a single file with du:

du -h somedisk.dmg

du reports actual disk usage, walking the file's real allocation rather than its logical length, which is exactly why it's the tool people reach for when a Finder size and a "real" size disagree. If du -h reports 6.2G for a file that Finder's Get Info calls 40 GB, the file is sparse, and the 40 GB number was never wrong — it was just describing the container's capacity, not the space actually consumed.

Where sparse files commonly show up

Sparse files aren't an obscure edge case; they are the normal representation for several common categories of file on a Mac:

  • Virtual machine disk images — a VM's virtual hard drive is usually created at its full configured size but only allocates blocks as the guest operating system actually writes data, so a "64 GB disk" for a fresh VM install might occupy only a few gigabytes at first.
  • Disk images generally.dmg and similar container formats are frequently sparse, reserving space up front while growing their real footprint only as content is added.
  • Some database files — certain database engines pre-allocate large files for their storage or write-ahead logs and rely on the filesystem's sparse-file support to avoid wasting space on regions that haven't been used yet.

In each case, the large nominal size is intentional — it reserves address space the application might need — and the small allocated size is the more honest measure of what the file is currently costing you.

APFS's support for sparse files

APFS supports sparse files natively, and its copy-on-write design actually makes sparse regions cheap to create and cheap to keep sparse under normal use: writing into an existing file only allocates new blocks for the specific ranges being written, rather than rewriting the whole file. This is one reason disk images and VM disks tend to behave well, space-wise, on an APFS volume compared to older filesystems with weaker sparse-file handling. It does mean, though, that operations which read and rewrite a sparse file wholesale — certain backup or compression utilities, for instance — can inadvertently "fill in" the holes, turning a mostly-empty sparse file into one that is fully allocated and much larger on disk than it used to be, even though its logical size never changed.

Conclusion

The gap between a file's reported size and its real disk footprint isn't a glitch — it's the filesystem doing exactly what sparse files are designed to do: reserving room without committing space until it's actually needed. The two numbers, logical and allocated, are both true at once; they're just answering different questions, and knowing which one you're looking at explains a lot of otherwise-confusing storage behaviour.

We built VolumeLens to show the number that actually matters when you're deciding what to clean up: allocated, on-disk size, not just the nominal figure a naive size sum would give you. It flags sparse files specifically, so a large-looking VM disk or disk image doesn't get mistaken for the thing that's actually filling your drive.