Languages / Java
Java static analysis: dead code, call graphs and complexity
To find dead code in Java, IntelliJ's unused declaration inspection and PMD work inside one project. repowise builds a call graph across the whole repository and knows how Spring, Jakarta EE, Quarkus and Micronaut reach code by annotation, so @Service beans, @RestController handlers and JPA entities are roots, not false positives. Findings carry confidence and an owner.
Last reviewed: November 2026
Full tier
What repowise detects in Java
Checked against the engine source. Every row runs locally with zero LLM calls.
| Files parsed | .java |
|---|---|
| Import resolution | Full. Package-to-file index, wildcard and static imports, Gradle settings subprojects and source sets, shared JVM index with Kotlin and Scala |
| Call graph | Resolved calls, inheritance and interface implementation |
| Dead code | Unreachable files, unused exports, zombie packages; reachability from main, stereotype annotations, META-INF/services and JPMS |
| Health markers | Complexity, class metrics, Extract Method plans |
| Performance markers | Spring Data findBy and JDBC calls in loops, string concatenation and Pattern.compile in loops, blocking I/O under a lock |
| Coverage reports | JaCoCo XML, Cobertura |
| Frameworks recognised | Spring (stereotypes, @Bean, constructor and field injection, Lombok constructors, request mappings, auto-configuration), Jakarta EE and javax (JAX-RS, CDI, Servlet, EJB, JPA), Quarkus, Micronaut, Camel, Android manifest |
Specific to this language
How Java code is read
Spring wiring is in the graph
A @Service that nothing calls directly is still used when Spring injects it. repowise records stereotype annotations, @Autowired, @Inject and @Resource fields, constructor injection including Lombok @RequiredArgsConstructor, @Bean factory methods, Spring Data repositories and @GetMapping or @RequestMapping handlers. AutoConfiguration.imports and spring.factories entries are roots.
Java and Kotlin share one index
Java and Kotlin (and Scala) files resolve through one JVM package index, so a Java class used only from Kotlin is reached, and the reverse. Gradle subprojects from settings.gradle and non-main source sets such as integrationTest are read.
Measured performance risk
The Java dialect flags Spring Data derived queries (findByX, countByX) on a repository and JDBC executeQuery or prepareStatement inside loops, regex compilation in loops and blocking I/O under a lock. Hibernate lazy-load N+1 is a stated ceiling: it is not detected.
Honest comparison
Native Java tools, and when they are enough
| Tool | What it covers |
|---|---|
| IntelliJ IDEA inspections | Unused declarations across a project, with Spring support in Ultimate. Runs in the IDE. |
| PMD | UnusedPrivateMethod, UnusedPrivateField and similar rules, plus CyclomaticComplexity. Private and local scope only for unused code. |
| SpotBugs | Bug patterns in bytecode, including some unused fields and dead stores. |
| JaCoCo | Line and branch coverage. Its XML report is read by repowise. |
| jdeps | Package and module dependencies from compiled classes. |
Where repowise adds value
- Public and file-level dead code across modules, not only private members.
- Framework roots for Spring, Jakarta EE, Quarkus and Micronaut, so beans and endpoints are not reported.
- Git history: hotspots, ownership and co-change on the same classes, and confidence that starts from file age.
- Runs headless with no build: no compile step, no JDK on the analysis machine.
How to run it
Analyse a Java repository
From the repository root. Agents get the same results through the get_dead_code, get_health and get_context MCP tools.
uv tool install repowise # or: pipx install repowise
repowise init --yes --no-prose # index the repo, no API key
repowise dead-code --min-confidence 0.7 --safe-only
repowise health --refactoring-targets
./gradlew test jacocoTestReport
repowise coverage add build/reports/jacoco/test/jacocoTestReport.xml
repowise serve # graph, health and dead code in a local dashboardQuestions
What people ask before they try it.
How do I generate a call graph for a Java project?
Run repowise init in the repository root. It parses every .java file without compiling, resolves imports through a package index, and records call, inheritance and implementation edges with a confidence each. Explore it with repowise serve or query it from an agent over MCP.
Will it report Spring beans as unused?
No. Classes with Spring stereotypes, injected fields and constructors, @Bean methods, Spring Data repositories and request-mapping handlers are reached through the framework, and @SpringBootApplication and main methods are roots.
Does it read JaCoCo coverage?
Yes. repowise coverage add takes a JaCoCo XML report, detected from its content. Coverage then feeds the health score, test reach on change risk and the coverage gates.
Do I need to build the project first?
No. repowise parses source with tree-sitter and needs no JDK, Maven or Gradle run. Coverage is the only input that comes from a build.
Does repowise send my Java code to an LLM?
No. Parsing, the call graph, dead code, health scores, performance markers and git history all run locally with zero LLM calls. repowise init --yes --no-prose needs no API key, and the analysis itself makes no network calls. Written documentation prose is the one opt-in step that uses a model, and it can run on a local Ollama model.
Related reading
Index a Java repository.
Public repositories are free on repowise.dev. The CLI is open source and runs on your machine with no API key.