Skip to main content
OracleOracleOJVMJavalicensingdiagnosticsupgrademigrationfeature usagemultitenant

Oracle JVM Installed: Is the Application Actually Using It?

Finding JAVAVM in DBA_REGISTRY proves Oracle JVM is installed — not that an application depends on it. A practical diagnostic framework for separating installation from active use.

15 min read

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_REGISTRY and V$OPTION establish 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_STATISTICS varies 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 = VALID confirms OJVM is installed and registered in the current container.
  • No row means OJVM is not registered in the current database or PDB.
  • STATUS = INVALID requires 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 JAVA call 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_OWNER owns Java classes and publishes them through three LANGUAGE JAVA functions. 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 JAVAVM in DBA_REGISTRY as proof of application dependency.
  • Do not treat absence from DBA_FEATURE_USAGE_STATISTICS as proof that the feature was never used — it is sampled, not exhaustive.
  • Do not run the checks only in CDB$ROOT and 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

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


Marios Pavlidis Principal Database Administrator