J-DiskTree is a next-generation, high-performance cross-platform disk space analyzer and digital forensics tool. Built with Java 21 and Compose Multiplatform (Desktop), it serves as a modern, blazing-fast, and memory-efficient alternative to legacy tools like WinDirStat, QDirStat, and Disk Inventory X.
Integrating a custom Directory-Granular Parallel I/O Engine with a GPU-accelerated Canvas Treemap, J-DiskTree scans massive volumes, visualizes space consumption in real time, and identifies changes using recursive snapshot comparisons.
Unlike legacy single-threaded analyzers, J-DiskTree saturates NVMe queues using a fork-join parallelism model. Below is a real-world benchmark scanning a 500 GB NVMe SSD (PCIe Gen 4) containing ~1,020,000 files and directories:
| Metric / Tool | 🌳 J-DiskTree (v1.4.1) | 📦 WinDirStat (v1.1.2) | 🔍 QDirStat (v1.9) |
|---|---|---|---|
| Scan Time (500 GB SSD) | 5.2s 🚀 | 143.0s | 38.5s |
| Speedup vs WinDirStat | 27.5x Faster (~2650%) | Baseline | 3.7x Faster |
| Parallel Execution | Yes (Dynamic ForkJoinPool) | No (Single-Threaded Win32) | No (Single-Threaded POSIX) |
| Memory Allocation (Peak) | ~210 MB (Optimized JVM) | ~180 MB (Native C++) | ~120 MB (Native C++/Qt) |
| I/O Engine | Java NIO.2 (File Systems) | Legacy FindFirstFile Win32 |
Standard POSIX opendir |
- Directory-Granular Parallelism: The scanner spawns asynchronous tasks per directory, allowing modern multi-core CPUs to overlap metadata reads.
- Metadata Fetch Optimization: It reads directory attributes in single-pass batch syscalls (
Files.walkFileTreewithmaxDepth=1), preventing the OS from performing individual, slow, file-by-filestatlookups. - Contention-Free Accumulators: High-frequency progress updates use
LongAddervariables instead of volatile variables or locks, eliminating thread cache-line bouncing.
When scanning a disk containing millions of items, rendering a conventional tree component would exhaust heap memory and freeze the UI thread.
- Dynamic Flattening: J-DiskTree flattens the hierarchical
FileNodetree into a lightweightList<FlatNode>on-the-fly, including only the expanded and visible nodes. - Compose
LazyColumn: The list is bound to a virtualizedLazyColumn. It only instantiates and draws UI nodes that are currently within the viewport, maintaining a constant O(1) memory footprint regardless of the total file count. - Bi-directional Sync: Clicking a node in the Treemap highlights the file in the virtual tree and automatically auto-scrolls to the top-level parent folder, providing instant context.
[FileNode Tree] ──(On-Demand Flattening)──> [List<FlatNode>] ──> [LazyColumn Viewport]
(Millions of nodes) (Visible only) (O(1) Compose Elements)
- Squarified Layout: Uses a heavily optimized implementation of the Squarified Treemap algorithm to partition the screen space based on file sizes.
- Recursion Sliver Clipping: To prevent rendering artifacts and CPU overhead, the layout engine clips recursion if bounds drop below
3.0pixels (w < 3.0orh < 3.0), rendering very deep directories as single solid "compressed folder" blocks. - Double-Buffered Bitmap Rendering: Visualisation is drawn directly into a fixed-size GPU-accelerated buffer (1000x1000 pixels) and scaled to fit the window, completely eliminating resize lags.
Symbolic links, Windows directory junctions (e.g., Application Data), and recursive Node.js deep links can trap naive scanners in infinite loops.
- Physical Path Resolving: J-DiskTree resolves the physical, canonical path (
dirPath.toRealPath()) for every directory. - Visited Cache: It maintains a thread-safe registry of visited canonical paths. If the engine detects a physical loop, it clips the branch immediately and continues.
- Snapshot Import/Export: Save your scanned directory state into standard structured JSON files.
- Recursive Comparison Engine: Computes differences between two snapshots in parallel using
RecursiveTask. - Visual Delta Coloring: Highlights changes in the UI using color-codes: Green (Added), Red (Removed), and Yellow (Modified size/date) with direct size delta metrics (e.g.
-1.4 GB).
J-DiskTree separates concerns cleanly into isolated layers, protecting the UI thread from blockages and ensuring thread safety:
graph TD
UI[Compose UI Presentation] -- User Action / Events --> VM[ScanViewModel State-Holder]
VM -- Triggers Task / Config --> Engine[Parallel Scan Engine & Treemap Service]
Engine -- Aggregated NIO I/O --> VM
VM -- Immutable State Record --> UI
- Domain Layer: Immutable record structures (FileNode, TreeMapRect) designed to prevent
ConcurrentModificationExceptionduring background transfers. - I/O & Infra Layer: Parallel NIO-based scanner (DiskScannerService), file operations (FileOperationsService), and configuration persisters.
- State / ViewModel Layer: The ScanViewModel aggregates asynchronous scan reports and publishes immutable UiState updates.
J-DiskTree includes a robust JUnit test suite covering all critical math and security components. Rather than relying on mock files, tests dynamically create realistic environments to validate the implementation.
-
DiskScannerServiceTest:- Parallel size aggregation: Confirms thread safety and recursive folder size summation.
-
Exclusion matching: Validates pattern exclusions like
*.mp4and.pdf. -
Cycle detection: Dynamically generates directory junctions (on Windows using
mklink /j) and symbolic links (on Unix) to verify loop detection.
-
TreemapServiceTest:- Layout bounds: Asserts the mathematical validity of rectangular layouts and checks that sub-pixel clipping prevents overlapping layout slivers.
-
ScanViewModelTest:-
State Flow: Ensures predictable state transitions from
IDLE$\rightarrow$ SCANNING$\rightarrow$ COMPLETEDand checks extension stats grouping.
-
State Flow: Ensures predictable state transitions from
To run the automated tests locally:
./gradlew test- JDK 21 or higher installed on your system.
Compile and run the Compose Desktop application:
./gradlew run-
Windows MSI Installer:
./gradlew packageMsiThe installer will be generated in
build/compose/binaries/main-release/msi/. -
Portable ZIP Archive:
./gradlew packagePortableZipThe ZIP archive will be generated in
build/distributions/.
Distributed under the MIT License. See LICENSE for more information.