DEX: What It Is, Structure and How Bytecode Works

Author: IT Sectr Published: 2026-04-15 Reading time: 8 min

DEX (Dalvik Executable) is a bytecode format into which the source code of Android applications in Java and Kotlin is compiled. DEX files are executed by the Dalvik virtual machine (up to Android 4.4) or Android Runtime (ART, starting with Android 5.0). According to Android Open Source Project, 2026, the DEX format provides on average 30% more compact code representation compared to standard JVM Java bytecode.

Key Takeaways

  • DEX is a bytecode format for Android, executed on Dalvik or ART.
  • Compactness — DEX takes up 30% less space than standard Java bytecode.
  • Multidex — a mechanism to bypass the 65536 method limit in a single DEX file.
  • ART — Android Runtime, which replaced Dalvik, compiles DEX into native code during installation.
  • D8 — a modern Java/Kotlin to DEX compiler, replacing DX since 2018.

What Is DEX and Why It Is Needed

DEX (Dalvik Executable) is a bytecode format designed specifically for Android mobile devices. Unlike standard Java bytecode (.class files), DEX is optimized for limited resources: less memory, smaller size, and faster class loading.

From Java to DEX

Source code in Java or Kotlin is compiled by javac/kotlinc into standard .class files (Java bytecode). Then the d8 tool (or earlier dx) converts .class into one or more DEX files. This conversion is not a simple repackaging — d8 performs optimizations: merges constant pools, rewrites instructions into register architecture, and removes duplicate data.

Architectural Features

DEX uses a register-based architecture (as opposed to stack-based JVM). Each method has a fixed number of registers (up to 65536). DEX instructions are shorter — on average 2 bytes vs 1–4 bytes in JVM. This yields more compact code: a typical application shrinks from 10–15 MB of .class to 4–6 MB of .dex.

DEX File Structure: Sections and Header

A DEX file has a strictly defined binary structure. Each file starts with a header and contains several sections that reference each other through offsets.

SectionPurpose
headerHeader: magic, checksum, signature, section sizes and offsets
string_idsString table: class, method, and field names
type_idsTypes: references to type string identifiers
proto_idsMethod prototypes: return type and parameters
field_idsClass fields: class, type, name
method_idsMethods: class, prototype, name
class_defsClass definitions: flags, superclass, interfaces, data offsets
dataActual data: method code, annotations, debug info

DEX Header

The magic number of DEX is `dex\n035\0` (version 035). Other versions: 036, 037, 038 (for Android 8.0+). The header is 0x70 bytes in size and contains a SHA-1 checksum and offsets of all sections. Header validation is the first step when the virtual machine loads a DEX.

Constant Pools

string_ids, type_ids, proto_ids, field_ids, method_ids — these are indexed tables. Instead of storing full names in method code, a 4-byte index is used. This is a key optimization: if a class is referenced 100 times, its name is stored once in string_ids. dex2oat further optimizes these tables during ART compilation.

The Process of Compiling Java and Kotlin into DEX

The process of converting source code into DEX consists of several stages. The modern toolchain uses the D8 compiler, which replaced DX in 2018 with Android Gradle Plugin 3.2.

Stage 1: Compilation into .class

javac (for Java) or kotlinc (for Kotlin) compiles source code into .class files. Each class is a separate .class file in Java bytecode. At this stage, type checking, generation of bridge methods, and constant inlining are performed.

Stage 2: D8 Compilation

D8 takes all .class files and transforms them into DEX bytecode. D8 performs several optimizations: removes unused method arguments, merges constant pools from different .class files into one global DEX pool, and converts JVM stack instructions into Dalvik register instructions.

kotlin
// Kotlin source code
data class User(
    val name: String,
    val email: String
)

fun greet(user: User): String {
    return "Hello, ${user.name}!"
}

After D8 compilation, this code turns into compact DEX instructions: const-string for loading strings, iget-object for accessing object fields, invoke-virtual for calling StringBuilder.append.

D8 vs DX

D8 is 2–3 times faster than DX, generates more compact DEX (5–10% smaller), and better optimizes Kotlin-specific constructs (inline functions, lambdas). DX was declared deprecated in 2018 and removed from Android Gradle Plugin 8.0.

Dalvik vs ART: How DEX Execution Changed

The execution of DEX code in Android has gone through two stages: the original Dalvik virtual machine (Android 2.2–4.4) and Android Runtime ART (Android 5.0+). The difference in compilation approach is fundamental.

Dalvik VM: JIT Compilation

Dalvik used Just-In-Time (JIT) compilation: DEX bytecode was interpreted, and frequently called methods were compiled into native code on the fly. Pros — fast installation. Cons — slower startup and constant CPU overhead for JIT.

ART: AOT Compilation

ART (Android Runtime) compiles DEX into native code during application installation via dex2oat. This is an Ahead-Of-Time (AOT) approach: installation takes longer, but startup is faster and power consumption is lower. Since Android 7.0, ART uses a hybrid approach — AOT + JIT + Profile Guided Optimization.

dex2oat: Conversion During Installation

The dex2oat tool runs when an application is installed or updated. It compiles DEX into an ELF file with native code for the device’s architecture. The result — .oat and .art files in the /data/dalvik-cache/ directory. Google continuously improves dex2oat: Android 14 added optimizations for foldable devices.

Multidex: Overcoming the 64K Method Limit

The limit of 65536 methods per DEX file is a legacy of the Dalvik architecture. The method_ids field in the DEX header takes 4 bytes, giving a maximum of 2^16 = 65536 unique references. Modern applications with Google Play Services, Firebase, and other SDKs easily exceed this limit.

Multidex Mechanism

Multidex is a mechanism for splitting code across multiple DEX files. The main classes.dex contains entry points (Application class, main Activity), the rest are classes2.dex, classes3.dex, and so on. At startup, classes from additional DEX files are loaded via DexClassLoader.

kotlin
// build.gradle.kts — enabling multidex
android {
    defaultConfig {
        multiDexEnabled = true
    }
}

// Application class with multidex support
class MyApp : Application() {
    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(base)
        MultiDex.install(this)
    }
}

Multidex Issues

Loading additional DEX files during application startup can cause ANR (Application Not Responding) on devices with Android versions below 5.0. Recommendation — use multidex only when necessary and minimize dependencies to avoid exceeding the limit.

DEX Optimization: ProGuard, R8 and Obfuscation

Optimization of DEX is a standard step in building a release Android application. R8 and ProGuard tools reduce DEX size, obfuscate code, and remove unused classes.

R8 vs ProGuard

R8 is the successor to ProGuard, built into Android Gradle Plugin since 2019. R8 performs minification, obfuscation, and optimization in a single pass, while ProGuard required two stages: ProGuard → D8. ProGuard is still supported, but Google recommends R8 for new projects.

R8 removes unused classes, methods, and fields, renames them to short names (a, b, c), inlines functions, and eliminates dead code. The result — DEX is reduced by 20–40% without loss of functionality.

R8 Rules

R8 configuration is specified in the proguard-rules.pro file. Developers can specify which classes cannot be renamed (for example, for reflection or Gson serialization). Firebase and other SDKs provide their own rules in their dependencies.

DEX Decompilation: Tools and Protection

DEX can be decompiled back into Java code. This is a key security concern for Android applications: without obfuscation, code is restored to a level close to the original.

Decompilation Tools

JADX is the most popular DEX to Java decompiler. It restores class names, methods, fields, and most of the logic. apktool decompiles DEX into smali code (Dalvik assembler) — a low-level representation close to the original instructions. Bytecode Viewer combines multiple decompilers in one interface.

Protection Methods

Obfuscation with R8/ProGuard is the first line of defense: class and method names become unreadable. DexGuard is a commercial tool with additional methods: string encryption, integrity checking, anti-tamper. Control Flow Obfuscation (O-LLVM) changes code structure while preserving functionality, making analysis much more difficult.

Frequently Asked Questions

How is DEX different from Java bytecode?

DEX uses a register-based architecture instead of stack-based JVM, has a more compact format (30% smaller), merges all .class files into one file with a single constant pool, and uses 16-bit indexes instead of 8-bit ones.

What is smali?

Smali is an assembler for DEX bytecode. Each DEX instruction has a text representation in smali format. The baksmali tool converts DEX to smali (disassembly), and smali assembles smali back into DEX.

How to check the number of methods in DEX?

The Gradle countMethods task or the dex-method-counts plugin show the number of methods in each DEX file. The adb shell command with dumpsys also displays statistics of loaded DEX files for installed applications.

Does the number of DEX files affect performance?

Yes, on devices below Android 8.0, multiple DEX files slow down application startup because each additional file is loaded separately. On ART with Android 8.0+, the difference is minimal thanks to dex2oat compilation into a single .oat file.

Can DEX run without Android?

Yes, there are projects such as dexplorer and Android-compatible JVM implementations that can execute DEX bytecode outside of Android. However, most DEX files use the Android API, making them unsuitable for running on a standard JVM.

Summary

  • DEX is an Android bytecode format with register-based architecture and compact code representation.
  • Structure includes a header, identifier tables, and a data section with instructions.
  • Compilation to DEX is done via D8: .class → DEX with optimizations and constant pool merging.
  • ART compiles DEX into native code during installation (AOT), speeding up application startup.
  • Multidex solves the 65536 method limit by splitting into multiple DEX files.
  • Optimization — R8 reduces DEX by 20–40%, obfuscates names, and removes dead code.
  • Protection — obfuscation with R8/ProGuard, DexGuard, and O-LLVM prevents DEX decompilation.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also