Three weeks before a major Oracle upgrade, the licensing team sends an email. JAVAVM is listed in DBA_REGISTRY. Is that a separately licensed option? Does any application actually use it? Can it be removed before the migration?
The DBA pulls DBA_REGISTRY, sees JAVAVM with STATUS = VALID, and is no closer to answering any of those questions. The component is present. Whether the application depends on it is a different investigation entirely.
The answer
Oracle JVM being installed and Oracle JVM being actively used by an application are separate states that require separate evidence to distinguish. No single Oracle view answers both. A defensible assessment must work through four discrete questions — availability, installation, Oracle-detected usage, and application dependency — using different data sources for each, and must be explicit about what the available evidence does and does not prove.
What you will learn
- Why
DBA_REGISTRYandV$OPTIONestablish capability, not application dependency - Which views provide escalating evidence strength, from availability through to execution history
- How to identify application-owned Java objects, published call specifications, and automated invocation paths
- How to produce a combined OJVM assessment report and interpret the results honestly
Scope: Oracle Database 12c through 21c, non-CDB and multitenant (CDB/PDB). Behavior of
DBA_FEATURE_USAGE_STATISTICSvaries by Oracle version and patch level. SQL history and ASH queries require the Diagnostics Pack licence where applicable. Validate all findings in the target environment — component presence and feature detection logic differ across releases.
Installed is not the same as used
This distinction matters most during upgrades, licensing reviews, and platform migrations, when someone needs to justify keeping or removing a component.
V$OPTION value = TRUE → capability is available in the Oracle binary
DBA_REGISTRY row exists → component is installed in the database
DBA_FEATURE_USAGE_STATISTICS → Oracle detected usage during sampling
DBA_OBJECTS / DBA_SOURCE → application code depends on Java at rest
V$SQL / AWR / ASH → execution evidence from SQL history
Each layer answers a different question. The evidence matrix at the end of this article maps these levels explicitly.
An Oracle-created database may contain thousands of Java objects under Oracle-maintained schemas (SYS, XDB, MDSYS, and others) even when no business application has ever loaded a Java class. The presence of those objects does not establish an application dependency.
Check availability and installation
V$OPTION: what the Oracle binary supports
SELECT parameter,
value
FROM v$option
WHERE UPPER(parameter) LIKE '%JAVA%'
ORDER BY parameter;
VALUE = 'TRUE' means the Oracle software was compiled with that capability. It does not mean the database component is installed, and it does not establish application usage.
Oracle documents V$OPTION as the view that displays database options and features. Some rows represent separately licensed options; others represent features included with a database edition or product. Do not infer a licensing conclusion from V$OPTION alone.
DBA_REGISTRY: what is installed in this database
SELECT comp_id,
comp_name,
version,
status,
modified
FROM dba_registry
WHERE comp_id = 'JAVAVM';
Typical result when OJVM is installed:
COMP_ID COMP_NAME VERSION STATUS
------- -------------------------------- ------------ ------
JAVAVM JServer JAVA Virtual Machine 19.0.0.0.0 VALID
- A row with
STATUS = VALIDconfirms OJVM is installed and registered in the current container. - No row means OJVM is not registered in the current database or PDB.
STATUS = INVALIDrequires investigation before drawing any other conclusions.
This still confirms only that the component is present — not that user applications depend on it.
DBMS_JAVA: a direct installation check
OJVM installation creates the DBMS_JAVA package under SYS. Its presence or absence is a direct cross-check.
SELECT owner,
object_name,
object_type,
status
FROM dba_objects
WHERE owner = 'SYS'
AND object_name = 'DBMS_JAVA';
Review Oracle’s feature-usage statistics
DBA_FEATURE_USAGE_STATISTICS contains sampled feature detection data collected automatically by the database.
SELECT name,
detected_usages,
total_samples,
currently_used,
first_usage_date,
last_usage_date,
last_sample_date,
feature_info
FROM dba_feature_usage_statistics
WHERE UPPER(name) LIKE '%JAVA%'
OR UPPER(name) LIKE '%JVM%'
ORDER BY name;
Key columns:
| Column | Meaning |
|---|---|
DETECTED_USAGES |
Number of samples in which Oracle detected usage |
TOTAL_SAMPLES |
Total times Oracle checked |
CURRENTLY_USED |
Whether usage was detected in the most recent sample |
FIRST_USAGE_DATE |
First detected usage |
LAST_USAGE_DATE |
Latest detected usage |
FEATURE_INFO |
Feature-specific detail, where available |
This data is useful but has firm limits. It is sampled rather than event-by-event. It depends on Oracle’s internal detection logic. It is not a complete execution history and is not a substitute for a formal licensing assessment.
A row showing no detected usage is not absolute proof that the feature was never used within the available retention period.
Find application-owned Java objects
The strongest static evidence is Java code stored under non-Oracle application schemas.
Summary by schema
SELECT o.owner,
o.object_type,
COUNT(*) AS object_count,
MIN(o.created) AS earliest_created,
MAX(o.last_ddl_time) AS latest_ddl
FROM dba_objects o
JOIN dba_users u
ON u.username = o.owner
WHERE o.object_type LIKE 'JAVA%'
AND u.oracle_maintained = 'N'
GROUP BY o.owner,
o.object_type
ORDER BY o.owner,
o.object_type;
Using ORACLE_MAINTAINED = 'N' is preferable to a hard-coded exclusion list. It excludes Oracle-supplied schemas without requiring maintenance when new Oracle-managed accounts appear.
Possible Java object types: JAVA SOURCE, JAVA CLASS, JAVA RESOURCE, JAVA DATA.
Individual objects
SELECT o.owner,
o.object_name,
o.object_type,
o.status,
o.created,
o.last_ddl_time
FROM dba_objects o
JOIN dba_users u
ON u.username = o.owner
WHERE o.object_type LIKE 'JAVA%'
AND u.oracle_maintained = 'N'
ORDER BY o.owner,
o.object_type,
o.object_name;
Finding that an application schema owns 40 JAVA CLASS objects and 3 JAVA SOURCE objects is materially stronger evidence than JAVAVM appearing in DBA_REGISTRY. It indicates an application-level dependency that must be resolved before removing OJVM or migrating to a platform without equivalent support.
Find PL/SQL call specifications that publish Java methods
Java classes loaded into Oracle are not automatically callable from SQL or PL/SQL. Oracle documents loading Java classes and publishing them to SQL as separate steps. The publication uses a call specification containing LANGUAGE JAVA.
A wrapper that publishes a Java method looks like this:
CREATE OR REPLACE FUNCTION app_hash (
p_value VARCHAR2
) RETURN VARCHAR2
AS LANGUAGE JAVA
NAME 'com.example.Hashing.hash(java.lang.String)
return java.lang.String';
/
Search for these wrappers across non-Oracle schemas:
SELECT DISTINCT
s.owner,
s.name,
s.type
FROM dba_source s
JOIN dba_users u
ON u.username = s.owner
WHERE UPPER(s.text) LIKE '%LANGUAGE JAVA%'
AND u.oracle_maintained = 'N'
ORDER BY s.owner,
s.name;
A published call specification is strong evidence of an intended Java entry point — an explicit act of exposing a Java method to SQL or PL/SQL callers.
Inspect dependencies on Java objects
A Java object may exist but no longer be referenced. Use DBA_DEPENDENCIES to determine whether database objects depend on it.
Non-Oracle objects that reference Java objects:
SELECT d.owner,
d.name,
d.type,
d.referenced_owner,
d.referenced_name,
d.referenced_type
FROM dba_dependencies d
JOIN dba_users u
ON u.username = d.owner
WHERE d.referenced_type LIKE 'JAVA%'
AND u.oracle_maintained = 'N'
ORDER BY d.owner,
d.name;
Dependency data does not prove runtime execution, particularly when calls are constructed dynamically, but it establishes the application dependency chain that would break if OJVM were removed.
Look for execution evidence in SQL history
Static objects establish capability. Execution evidence establishes use.
Current shared pool
Search for the Java wrapper names identified from DBA_SOURCE. Replace APP_HASH with each wrapper found:
SELECT sql_id,
parsing_schema_name,
executions,
first_load_time,
last_active_time,
module,
action,
SUBSTR(sql_text, 1, 1000) AS sql_text
FROM v$sql
WHERE UPPER(sql_text) LIKE '%APP_HASH%'
ORDER BY last_active_time DESC;
Searching for the generic phrase LANGUAGE JAVA in V$SQL is rarely productive. Application sessions call the wrapper by name, not by its implementation keyword.
AWR SQL history (Diagnostics Pack required)
SELECT s.sql_id,
s.parsing_schema_name,
MIN(sn.begin_interval_time) AS first_seen,
MAX(sn.end_interval_time) AS last_seen,
SUM(s.executions_delta) AS executions
FROM dba_hist_sqlstat s
JOIN dba_hist_snapshot sn
ON sn.dbid = s.dbid
AND sn.instance_number = s.instance_number
AND sn.snap_id = s.snap_id
JOIN dba_hist_sqltext t
ON t.dbid = s.dbid
AND t.sql_id = s.sql_id
WHERE UPPER(t.sql_text) LIKE '%APP_HASH%'
GROUP BY s.sql_id,
s.parsing_schema_name
ORDER BY last_seen DESC;
AWR can provide historical evidence, but its retention is finite and its SQL statistics are snapshot-based. Absence from AWR does not prove the procedure was never called.
ASH: recent runtime evidence (Diagnostics Pack required)
Active Session History can identify sessions executing a known SQL ID. Translate PL/SQL object IDs back to object names for context:
SELECT ash.sample_time,
ash.session_id,
ash.sql_id,
entry_obj.owner AS entry_owner,
entry_obj.object_name AS entry_object,
current_obj.owner AS current_owner,
current_obj.object_name AS current_object,
ash.module,
ash.action
FROM dba_hist_active_sess_history ash
LEFT JOIN dba_objects entry_obj
ON entry_obj.object_id = ash.plsql_entry_object_id
LEFT JOIN dba_objects current_obj
ON current_obj.object_id = ash.plsql_object_id
WHERE ash.sample_time >= SYSDATE - 30
ORDER BY ash.sample_time DESC;
ASH is sampled activity. Short Java executions may not appear in it. Finding a wrapper in ASH is good evidence of execution; absence from ASH is not proof of non-use.
Check scheduler jobs and triggers
A Java wrapper may be invoked automatically, not by direct application calls. This step is critical — a wrapper can be completely absent from application code because it is called through a trigger, scheduler chain, or maintenance job.
DBMS_SCHEDULER jobs
SELECT owner,
job_name,
job_type,
job_action,
enabled,
state,
last_start_date,
next_run_date
FROM dba_scheduler_jobs
WHERE UPPER(job_action) LIKE '%APP_HASH%'
ORDER BY owner,
job_name;
Legacy DBMS_JOB entries
SELECT schema_user,
job,
what,
last_date,
next_date,
failures,
broken
FROM dba_jobs
WHERE UPPER(what) LIKE '%APP_HASH%'
ORDER BY schema_user,
job;
Database triggers
SELECT owner,
trigger_name,
triggering_event,
table_owner,
table_name,
status,
trigger_body
FROM dba_triggers
WHERE UPPER(trigger_body) LIKE '%APP_HASH%'
ORDER BY owner,
trigger_name;
Multitenant scope
In a multitenant database, run the investigation in the correct container.
-- Confirm current container
SELECT SYS_CONTEXT('USERENV', 'CON_NAME') AS container_name FROM dual;
-- OJVM installation across all containers (from CDB$ROOT)
SELECT con_id,
comp_id,
comp_name,
version,
status
FROM cdb_registry
WHERE comp_id = 'JAVAVM'
ORDER BY con_id;
-- Application-owned Java objects across all containers (from CDB$ROOT)
SELECT o.con_id,
o.owner,
o.object_type,
COUNT(*) AS object_count
FROM cdb_objects o
JOIN cdb_users u
ON u.con_id = o.con_id
AND u.username = o.owner
WHERE o.object_type LIKE 'JAVA%'
AND u.oracle_maintained = 'N'
GROUP BY o.con_id,
o.owner,
o.object_type
ORDER BY o.con_id,
o.owner,
o.object_type;
Do not run the checks only in CDB$ROOT and assume the result applies to every PDB. An application PDB may contain user Java objects even when the CDB root does not.
Combined assessment report
The following script runs the most useful static checks in a single pass and produces a structured output suitable for a change-management record or upgrade assessment.
SET PAGESIZE 1000
SET LINESIZE 220
SET LONG 100000
SET LONGCHUNKSIZE 100000
PROMPT
PROMPT === DATABASE AND CONTAINER ===
PROMPT
SELECT name,
db_unique_name,
database_role,
open_mode,
platform_name
FROM v$database;
SELECT SYS_CONTEXT('USERENV', 'CON_NAME') AS container_name
FROM dual;
PROMPT
PROMPT === JAVA OPTION AVAILABILITY ===
PROMPT
SELECT parameter,
value
FROM v$option
WHERE UPPER(parameter) LIKE '%JAVA%'
ORDER BY parameter;
PROMPT
PROMPT === OJVM COMPONENT ===
PROMPT
SELECT comp_id,
comp_name,
version,
status,
modified
FROM dba_registry
WHERE comp_id = 'JAVAVM';
PROMPT
PROMPT === DBMS_JAVA STATUS ===
PROMPT
SELECT owner,
object_name,
object_type,
status
FROM dba_objects
WHERE owner = 'SYS'
AND object_name = 'DBMS_JAVA';
PROMPT
PROMPT === FEATURE-USAGE STATISTICS ===
PROMPT
SELECT name,
detected_usages,
total_samples,
currently_used,
first_usage_date,
last_usage_date,
last_sample_date,
feature_info
FROM dba_feature_usage_statistics
WHERE UPPER(name) LIKE '%JAVA%'
OR UPPER(name) LIKE '%JVM%'
ORDER BY name;
PROMPT
PROMPT === APPLICATION-OWNED JAVA OBJECTS ===
PROMPT
SELECT o.owner,
o.object_type,
COUNT(*) AS object_count,
MIN(o.created) AS earliest_created,
MAX(o.last_ddl_time) AS latest_ddl
FROM dba_objects o
JOIN dba_users u
ON u.username = o.owner
WHERE o.object_type LIKE 'JAVA%'
AND u.oracle_maintained = 'N'
GROUP BY o.owner,
o.object_type
ORDER BY o.owner,
o.object_type;
PROMPT
PROMPT === JAVA CALL SPECIFICATIONS ===
PROMPT
SELECT DISTINCT
s.owner,
s.name,
s.type
FROM dba_source s
JOIN dba_users u
ON u.username = s.owner
WHERE UPPER(s.text) LIKE '%LANGUAGE JAVA%'
AND u.oracle_maintained = 'N'
ORDER BY s.owner,
s.name;
PROMPT
PROMPT === DEPENDENCIES ON JAVA OBJECTS ===
PROMPT
SELECT d.owner,
d.name,
d.type,
d.referenced_owner,
d.referenced_name,
d.referenced_type
FROM dba_dependencies d
JOIN dba_users u
ON u.username = d.owner
WHERE d.referenced_type LIKE 'JAVA%'
AND u.oracle_maintained = 'N'
ORDER BY d.owner,
d.name;
This report establishes whether Java capability is available, OJVM is installed and valid, Oracle has detected Java-related feature usage, application schemas own Java objects, PL/SQL wrappers expose Java methods, and application objects depend on Java objects. It does not provide a complete historical execution audit.
How to interpret the results
Do not reach for a binary answer. Use an evidence matrix.
| Finding | Meaning | Evidence strength |
|---|---|---|
Java-related V$OPTION row is TRUE |
Java capability is available in the Oracle binary | Low |
JAVAVM row in DBA_REGISTRY |
OJVM is installed | Low |
JAVAVM status is VALID |
Installed component is valid | Low |
| Feature-usage statistics show Java usage | Oracle detected usage during sampling | Medium |
| Application schema owns Java objects | Application-level Java code exists at rest | High |
LANGUAGE JAVA call specification exists |
Java method is published to SQL/PL/SQL | High |
| Application objects reference Java objects | Database dependency chain exists | High |
| Wrapper appears in V$SQL or AWR history | Historical invocation evidence exists | High |
| Wrapper found in scheduler or trigger | Automated invocation path confirmed | High |
| Unified Audit records execution | Specific execution evidence | Very high |
Writing the conclusion
Precision in wording matters. Two examples:
No application dependency found:
OJVM is installed and valid. Oracle-maintained schemas contain Java objects, but no non-Oracle schema owns Java objects, no
LANGUAGE JAVAcall specifications were found, and no execution evidence was identified in the available SQL history and AWR retention period. Based on the evidence reviewed, no application dependency on OJVM was identified. This does not prove OJVM was never used outside the available retention window.
Active dependency confirmed:
OJVM is installed and actively required.
APP_OWNERowns Java classes and publishes them through threeLANGUAGE JAVAfunctions. One function is called by a DBMS_SCHEDULER job that appears in AWR SQL history. Removing OJVM would break the application.
The difference between “no usage identified” and “never used” is not pedantry — it is the difference between a defensible finding and an overstatement.
In practice
This pattern appears most often during:
- Upgrade assessments, where the team must confirm which components are genuinely required before removing or disabling options.
- Licensing reviews, where the question is whether a separately licensed option is actively consumed.
- Platform migrations, where the target environment may not support Oracle JVM, or where an equivalent capability must be confirmed before decommissioning the source.
- Security reviews, where OJVM represents an extension of the Oracle execution environment that increases the privilege surface area.
In all four contexts, the risk of false confidence runs in both directions: claiming “not used” without sufficient evidence, or retaining an unnecessary component indefinitely because the question was never properly investigated.
Track future Java executions with auditing
Oracle’s feature-usage view is not an execution audit trail. For a defensible future record, audit the PL/SQL wrapper that publishes the Java method directly.
In unified auditing environments:
-- Create the policy
CREATE AUDIT POLICY audit_app_java_calls
ACTIONS EXECUTE ON app_owner.app_hash;
-- Enable it
AUDIT POLICY audit_app_java_calls;
-- Query the results
SELECT event_timestamp,
dbusername,
action_name,
object_schema,
object_name,
client_program_name,
module,
action,
sql_text,
return_code
FROM unified_audit_trail
WHERE object_schema = 'APP_OWNER'
AND object_name = 'APP_HASH'
ORDER BY event_timestamp DESC;
In traditional auditing environments:
AUDIT EXECUTE ON app_owner.app_hash BY ACCESS;
Auditing the wrapper is more reliable than searching for generic OJVM activity because the database records execution of the actual application entry point, not a sampled approximation.
Before enabling auditing in production, assess volume and storage requirements. Audit specific wrappers rather than every execution across an entire high-activity schema.
Do not do this
- Do not treat
JAVAVMinDBA_REGISTRYas proof of application dependency. - Do not treat absence from
DBA_FEATURE_USAGE_STATISTICSas proof that the feature was never used — it is sampled, not exhaustive. - Do not run the checks only in
CDB$ROOTand assume the result represents every PDB. - Do not write “OJVM is not used” when the available evidence supports only “no OJVM usage was identified in the data reviewed.”
- Do not skip checking scheduler jobs and triggers — a wrapper may be called exclusively through automated paths, never appearing in direct application SQL.
Official references
- Oracle Database Reference: V$OPTION
- Oracle Database Reference: DBA_REGISTRY
- Oracle Database Reference: DBA_FEATURE_USAGE_STATISTICS
- Oracle Database Java Developer’s Guide: Loading and Publishing Java Stored Procedures
- Oracle Database Reference: UNIFIED_AUDIT_TRAIL
- Oracle Database Security Guide: Configuring Unified Auditing
Conclusion
Three weeks before the upgrade, the licensing team needed an answer. Not “JAVAVM is present” — they already knew that. They needed to know whether the application depended on it and whether the evidence was strong enough to act on.
That answer requires working through four separate questions with four different types of evidence. DBA_REGISTRY is the starting point, not the conclusion. The investigation ends only when the application-level dependency chain — owned Java objects, published call specifications, dependency references, and execution history — has been reviewed and documented.
If none of those layers show application code, the honest conclusion is: no dependency found in available evidence, not: Java is not used.
Continue reading
- Oracle Statspack: Installation, Snapshot Management, and Baseline Reporting
- Oracle Time Zone Layers: Why Changing One Setting Is Never Enough
Marios Pavlidis Principal Database Administrator