交互式JVM运行过程可视化系统

📋 项目背景

作为Java开发者,我们经常需要理解JVM的执行过程,但传统的学习方式存在以下问题:

  • 抽象概念难理解 - 堆、栈、方法区等概念很抽象
  • 执行过程不直观 - 无法直观看到字节码如何一步步执行
  • 内存变化看不到 - 对象创建、栈帧变化等过程是黑盒
  • 理论与实践脱节 - 书本知识与实际运行过程难以对应

🎯 解决的痛点

痛点1:JVM执行过程黑盒化

问题 : 学生只能通过文字和静态图表学习JVM原理 解决: 提供25步详细的JVM执行演示,从类加载到程序结束

痛点2:内存状态变化不可见

问题 : 无法直观看到堆内存、JVM栈的实时变化 解决: 实时显示内存区域状态,每一步都有对应的内存快照

痛点3:字节码与源码难对应

问题 : 不知道Java代码对应什么字节码指令 解决: 同时展示源码和字节码,当前执行指令高亮显示

痛点4:学习体验枯燥

问题 : 传统学习方式单调,难以保持学习兴趣 解决: 交互式操作,用户可以控制执行节奏,支持前进后退

🛠️ 解决方案

核心功能

  • 分步式执行 - 将JVM执行分解为25个清晰步骤
  • 多维度展示 - 源码、字节码、内存状态三位一体
  • 实时交互 - 支持步骤跳转、键盘控制
  • 可视化效果 - 现代化UI设计,直观易懂

技术实现

  • 前端技术: 纯HTML/CSS/JavaScript,无框架依赖
  • 布局设计: CSS Grid三层布局,响应式设计
  • 交互方式: 点击、键盘、胶囊式步骤导航
  • 视觉效果: 橘白配色,毛玻璃效果,平滑动画

示例程序

选择了包含对象创建、方法调用、算术运算的典型Java程序:

java 复制代码
public class JvmTest {
    public int add(){
        int a=1;
        int b=2;
        int c=(a+b)*10;
        return c;
    }
    public static void main(String[] args) {
        JvmTest jvmTest =new JvmTest();
        jvmTest.add();
    }
}

✨ 最终效果

源码信息

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JVM运行过程可视化 - JvmTest示例</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Monaco', 'Consolas', monospace;
            background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
            color: #333;
            min-height: 100vh;
        }

        .container {
            display: grid;
            grid-template-rows: auto auto 1fr;
            grid-template-columns: 1fr;
            height: 100vh;
            gap: 20px;
            padding: 20px;
        }

        .bottom-row {
            display: grid;
            grid-template-columns: 1fr 2fr;
            gap: 20px;
        }

        .top-bar {
            background: rgba(255,255,255,0.9);
            border: 1px solid rgba(255,140,0,0.2);
            border-radius: 10px;
            padding: 15px;
            backdrop-filter: blur(10px);
            box-shadow: 0 4px 20px rgba(255,140,0,0.1);
        }

        .top-steps {
            background: rgba(255,255,255,0.9);
            border: 1px solid rgba(255,140,0,0.2);
            border-radius: 10px;
            padding: 15px;
            backdrop-filter: blur(10px);
            box-shadow: 0 4px 20px rgba(255,140,0,0.1);
        }

        .steps-horizontal {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin-bottom: 15px;
            max-height: 200px;
            overflow-y: auto;
        }

        .step-horizontal {
            padding: 8px 12px;
            border-radius: 20px;
            cursor: pointer;
            transition: all 0.3s ease;
            font-size: 12px;
            white-space: nowrap;
            min-width: 120px;
            text-align: center;
        }

        .step-horizontal.pending {
            background: rgba(200,200,200,0.4);
            color: #666;
        }

        .step-horizontal.current {
            background: linear-gradient(45deg, #ff8c00, #ff7043);
            color: #fff;
            font-weight: bold;
            box-shadow: 0 4px 15px rgba(255,140,0,0.3);
        }

        .step-horizontal.completed {
            background: linear-gradient(45deg, #4caf50, #66bb6a);
            color: #fff;
        }

        .code-toggle {
            background: none;
            border: none;
            color: #e65100;
            font-size: 18px;
            font-weight: bold;
            cursor: pointer;
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 10px;
            transition: all 0.3s ease;
        }

        .code-toggle:hover {
            color: #ff8c00;
        }

        .code-toggle-icon {
            transition: transform 0.3s ease;
        }

        .code-toggle.collapsed .code-toggle-icon {
            transform: rotate(-90deg);
        }

        .code-content {
            overflow: hidden;
            transition: max-height 0.3s ease;
        }

        .code-content.collapsed {
            max-height: 0;
        }

        .left-panel {
            background: rgba(255,255,255,0.9);
            border: 1px solid rgba(255,140,0,0.2);
            border-radius: 10px;
            padding: 20px;
            backdrop-filter: blur(10px);
            box-shadow: 0 8px 32px rgba(255,140,0,0.1);
        }

        .right-panel {
            background: rgba(255,255,255,0.9);
            border: 1px solid rgba(255,140,0,0.2);
            border-radius: 10px;
            padding: 20px;
            backdrop-filter: blur(10px);
            box-shadow: 0 8px 32px rgba(255,140,0,0.1);
        }

        .steps-compact {
            max-height: 300px;
            overflow-y: auto;
        }

        .steps-compact .step {
            padding: 6px;
            margin: 3px 0;
            font-size: 13px;
        }

        .steps-compact .step > div:first-child {
            font-size: 12px;
        }

        .steps-compact .step > div:last-child {
            font-size: 10px;
        }

        .memory-panel {
            background: rgba(255,255,255,0.95);
            border: 2px solid rgba(255,140,0,0.3);
            border-radius: 15px;
            padding: 25px;
            backdrop-filter: blur(10px);
            box-shadow: 0 12px 40px rgba(255,140,0,0.15);
        }

        .memory-grid {
            display: grid;
            grid-template-rows: auto auto auto;
            gap: 15px;
        }

        .memory-row {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 15px;
        }

        .memory-row.single {
            grid-template-columns: 1fr;
        }

        .code-section {
            background: rgba(255,255,255,0.8);
            border: 1px solid rgba(255,140,0,0.2);
            border-radius: 10px;
            padding: 20px;
            backdrop-filter: blur(10px);
            box-shadow: 0 4px 20px rgba(255,140,0,0.08);
            flex: 1;
        }

        .bytecode-section {
            background: rgba(255,255,255,0.8);
            border: 1px solid rgba(255,140,0,0.2);
            border-radius: 10px;
            padding: 20px;
            backdrop-filter: blur(10px);
            box-shadow: 0 4px 20px rgba(255,140,0,0.08);
            flex: 1;
        }

        .step-list {
            list-style: none;
        }

        .step {
            padding: 10px;
            margin: 5px 0;
            border-radius: 5px;
            cursor: pointer;
            transition: all 0.3s ease;
            border-left: 4px solid transparent;
        }

        .step.pending {
            background: rgba(200,200,200,0.4);
            color: #666;
        }

        .step.current {
            background: linear-gradient(45deg, #ff8c00, #ff7043);
            border-left-color: #ff6f00;
            color: #fff;
            box-shadow: 0 4px 15px rgba(255,140,0,0.3);
        }

        .step.completed {
            background: linear-gradient(45deg, #4caf50, #66bb6a);
            border-left-color: #388e3c;
            color: #fff;
        }

        .controls {
            text-align: center;
            margin: 20px 0;
        }

        .btn {
            background: linear-gradient(45deg, #ff8c00, #ff7043);
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 25px;
            cursor: pointer;
            font-size: 16px;
            font-weight: 600;
            margin: 0 10px;
            transition: all 0.3s ease;
            box-shadow: 0 4px 15px rgba(255,140,0,0.2);
        }

        .btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 25px rgba(255,140,0,0.4);
            background: linear-gradient(45deg, #ff7043, #ff5722);
        }

        .btn:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        .code pre {
            color: #e65100;
            font-size: 13px;
            line-height: 1.4;
            background: rgba(255,245,238,0.6);
            padding: 10px;
            border-radius: 6px;
            border-left: 3px solid #ff8c00;
            margin: 0;
        }

        .bytecode pre {
            color: #f57c00;
            font-size: 12px;
            line-height: 1.5;
            background: rgba(255,243,224,0.6);
            padding: 12px;
            border-radius: 6px;
            border-left: 3px solid #ffb74d;
        }

        .memory-area {
            background: rgba(255,248,240,0.8);
            border: 1px solid rgba(255,140,0,0.3);
            border-radius: 8px;
            margin: 15px 0;
            padding: 18px;
            box-shadow: 0 2px 10px rgba(255,140,0,0.1);
        }

        .memory-title {
            color: #e65100;
            font-weight: bold;
            font-size: 16px;
            margin-bottom: 12px;
            border-bottom: 2px solid rgba(255,140,0,0.4);
            padding-bottom: 6px;
        }

        .stack-frame {
            background: rgba(76, 175, 80, 0.1);
            border: 2px solid #4caf50;
            border-radius: 8px;
            margin: 10px 0;
            padding: 12px;
            box-shadow: 0 2px 8px rgba(76, 175, 80, 0.2);
        }

        .local-vars, .operand-stack {
            margin: 8px 0;
        }

        .highlight {
            background: linear-gradient(45deg, #ffd54f, #ffcc02);
            color: #e65100;
            padding: 3px 6px;
            border-radius: 4px;
            font-weight: bold;
            box-shadow: 0 2px 4px rgba(255,193,7,0.3);
        }

        .heap-object {
            background: rgba(156, 39, 176, 0.1);
            border: 2px solid #9c27b0;
            border-radius: 8px;
            padding: 12px;
            margin: 8px 0;
            box-shadow: 0 2px 8px rgba(156, 39, 176, 0.2);
        }

        .step-info {
            background: rgba(255,248,240,0.9);
            border: 1px solid rgba(255,140,0,0.3);
            border-radius: 10px;
            padding: 18px;
            margin-top: 20px;
            box-shadow: 0 4px 15px rgba(255,140,0,0.1);
        }

        .step-info h4 {
            color: #e65100;
            margin-bottom: 10px;
        }

        .step-info p {
            color: #424242;
            line-height: 1.5;
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- 第一行:Java源代码区域 -->
        <div class="top-bar">
            <button class="code-toggle" onclick="toggleCode()">
                <span class="code-toggle-icon">▼</span>
                📝 Java源代码 (点击收起/展开)
            </button>
            <div class="code-content" id="codeContent">
                <div class="code">
                    <pre id="javaCode">public class JvmTest {
    public int add(){
        int a=1;
        int b=2;
        int c=(a+b)*10;
        return c;
    }
    public static void main(String[] args) {
        JvmTest jvmTest =new JvmTest();
        jvmTest.add();
    }
}</pre>
                </div>
            </div>
        </div>

        <!-- 第二行:执行步骤区域 -->
        <div class="top-steps">
            <h3>🚀 JVM执行步骤</h3>
            <div class="steps-horizontal" id="stepList">
                <!-- 步骤将通过JavaScript动态生成 -->
            </div>
            <div class="controls">
                <button class="btn" onclick="prevStep()" id="prevBtn">上一步</button>
                <button class="btn" onclick="nextStep()" id="nextBtn">下一步</button>
            </div>
        </div>

        <!-- 第三行:底部区域 -->
        <div class="bottom-row">
            <!-- 左侧 - 字节码区域 (1/3) -->
            <div class="left-panel">
                <h3>⚙️ 字节码指令</h3>
                <div class="bytecode">
                    <div id="mainBytecode">
                        <h4>main方法字节码:</h4>
                        <pre id="mainBytecodeContent">0: new           #2    // class JvmTest
3: dup                 // 复制栈顶引用
4: invokespecial #3    // 调用&lt;init&gt;构造器
7: astore_1            // 存储到局部变量1
8: aload_1             // 加载局部变量1
9: invokevirtual #4    // 调用add()方法
12: pop                // 弹出返回值
13: return             // 方法返回</pre>
                    </div>
                    <div id="addBytecode" style="margin-top: 20px;">
                        <h4>add方法字节码:</h4>
                        <pre id="addBytecodeContent">0: iconst_1      // 将常量1推入栈
1: istore_1      // 存储到局部变量1 (a=1)
2: iconst_2      // 将常量2推入栈  
3: istore_2      // 存储到局部变量2 (b=2)
4: iload_1       // 加载变量a
5: iload_2       // 加载变量b
6: iadd          // 执行加法 a+b
7: bipush 10     // 将常量10推入栈
9: imul          // 执行乘法 (a+b)*10
10: istore_3     // 存储到局部变量3 (c)
11: iload_3      // 加载变量c
12: ireturn      // 返回c的值</pre>
                    </div>
                </div>
            </div>

            <!-- 右侧 - JVM内存状态区域 (2/3) -->
            <div class="right-panel">
                <h3>🧠 JVM内存状态</h3>
                
                <div class="memory-grid">
                    <!-- 第一行:当前步骤说明 -->
                    <div class="memory-row single">
                        <div class="step-info" id="stepInfo">
                            <h4>当前步骤说明</h4>
                            <p id="stepDescription">点击"下一步"开始JVM执行演示</p>
                        </div>
                    </div>

                    <!-- 第二行:堆内存和JVM栈 -->
                    <div class="memory-row">
                        <div class="memory-area">
                            <div class="memory-title">堆内存 (Heap)</div>
                            <div id="heapMemory">
                                <div>暂无对象</div>
                            </div>
                        </div>

                        <div class="memory-area">
                            <div class="memory-title">JVM栈 (Stack)</div>
                            <div id="jvmStack">
                                <div>暂无栈帧</div>
                            </div>
                        </div>
                    </div>

                    <!-- 第三行:方法区和程序计数器 -->
                    <div class="memory-row">
                        <div class="memory-area">
                            <div class="memory-title">方法区 (Method Area)</div>
                            <div id="methodArea">
                                <div>JvmTest.class (待加载)</div>
                            </div>
                        </div>

                        <div class="memory-area">
                            <div class="memory-title">程序计数器 (PC Register)</div>
                            <div id="pcRegister">
                                <div>PC: 0</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 切换Java源代码显示/隐藏
        function toggleCode() {
            const codeContent = document.getElementById('codeContent');
            const toggleBtn = document.querySelector('.code-toggle');
            
            if (codeContent.classList.contains('collapsed')) {
                codeContent.classList.remove('collapsed');
                codeContent.style.maxHeight = codeContent.scrollHeight + 'px';
                toggleBtn.classList.remove('collapsed');
            } else {
                codeContent.style.maxHeight = '0';
                codeContent.classList.add('collapsed');
                toggleBtn.classList.add('collapsed');
            }
        }

        // 初始化代码区域高度
        function initCodeArea() {
            const codeContent = document.getElementById('codeContent');
            codeContent.style.maxHeight = codeContent.scrollHeight + 'px';
        }

        // JVM执行步骤数据
        const steps = [
            { id: 1, title: "JVM初始化", phase: "启动", description: "创建堆、栈、方法区等内存区域,初始化JVM运行环境" },
            { id: 2, title: "类加载器启动", phase: "启动", description: "初始化Bootstrap、Extension、Application类加载器" },
            { id: 3, title: "加载核心类", phase: "启动", description: "加载Object、String等Java核心类到方法区" },
            { id: 4, title: "加载JvmTest.class", phase: "类加载", description: "将JvmTest字节码文件读入方法区" },
            { id: 5, title: "验证字节码", phase: "类加载", description: "检查字节码格式和安全性,确保类文件正确" },
            { id: 6, title: "准备阶段", phase: "类加载", description: "为静态变量分配内存并设置默认值" },
            { id: 7, title: "解析阶段", phase: "类加载", description: "将符号引用转换为直接引用" },
            { id: 8, title: "初始化JvmTest类", phase: "类加载", description: "执行静态初始化块和静态变量赋值" },
            { id: 9, title: "创建main方法栈帧", phase: "main执行", description: "为main方法创建栈帧,设置局部变量表和操作数栈" },
            { id: 10, title: "执行new指令", phase: "main执行", description: "在堆中分配JvmTest对象内存空间" },
            { id: 11, title: "执行dup指令", phase: "main执行", description: "复制栈顶的对象引用" },
            { id: 12, title: "调用构造方法", phase: "main执行", description: "invokespecial调用<init>构造器初始化对象" },
            { id: 13, title: "存储对象引用", phase: "main执行", description: "astore_1将对象引用存储到局部变量表[1]" },
            { id: 14, title: "加载对象引用", phase: "main执行", description: "aload_1从局部变量表加载对象引用到栈顶" },
            { id: 15, title: "调用add方法", phase: "main执行", description: "invokevirtual调用JvmTest实例的add方法" },
            { id: 16, title: "弹出返回值", phase: "main执行", description: "pop指令弹出add方法的返回值" },
            { id: 17, title: "创建add方法栈帧", phase: "add执行", description: "为add方法创建新的栈帧并压入JVM栈顶" },
            { id: 18, title: "iconst_1", phase: "add执行", description: "将整型常量1推入操作数栈顶" },
            { id: 19, title: "istore_1", phase: "add执行", description: "将栈顶的1存储到局部变量表[1] (int a = 1)" },
            { id: 20, title: "iconst_2", phase: "add执行", description: "将整型常量2推入操作数栈顶" },
            { id: 21, title: "istore_2", phase: "add执行", description: "将栈顶的2存储到局部变量表[2] (int b = 2)" },
            { id: 22, title: "执行加法运算", phase: "add执行", description: "iload_1, iload_2加载a,b到栈,iadd执行a+b=3" },
            { id: 23, title: "执行乘法运算", phase: "add执行", description: "bipush 10推入常量10,imul执行(a+b)*10=30" },
            { id: 24, title: "存储结果", phase: "add执行", description: "istore_3将计算结果30存储到局部变量表[3] (int c = 30)" },
            { id: 25, title: "方法返回", phase: "add执行", description: "iload_3加载c,ireturn返回值30并销毁栈帧" }
        ];

        let currentStep = 0;

        // 初始化页面
        function initPage() {
            initCodeArea();
            renderStepList();
            updateMemoryState();
            updateButtons();
        }

        // 渲染步骤列表(水平排列)
        function renderStepList() {
            const stepList = document.getElementById('stepList');
            stepList.innerHTML = '';
            
            steps.forEach((step, index) => {
                const div = document.createElement('div');
                div.className = 'step-horizontal ' + getStepStatus(index);
                div.textContent = `${index + 1}. ${step.title}`;
                div.onclick = () => goToStep(index);
                stepList.appendChild(div);
            });
        }

        // 获取步骤状态
        function getStepStatus(index) {
            if (index < currentStep) return 'completed';
            if (index === currentStep) return 'current';
            return 'pending';
        }

        // 下一步
        function nextStep() {
            if (currentStep < steps.length - 1) {
                currentStep++;
                updateDisplay();
            }
        }

        // 上一步
        function prevStep() {
            if (currentStep > 0) {
                currentStep--;
                updateDisplay();
            }
        }

        // 跳转到指定步骤
        function goToStep(step) {
            if (step >= 0 && step < steps.length) {
                currentStep = step;
                updateDisplay();
            }
        }

        // 更新显示
        function updateDisplay() {
            renderStepList();
            updateStepDescription();
            updateMemoryState();
            updateButtons();
            highlightBytecode();
        }

        // 更新步骤描述
        function updateStepDescription() {
            const stepInfo = steps[currentStep];
            document.getElementById('stepDescription').textContent = stepInfo.description;
        }

        // 更新按钮状态
        function updateButtons() {
            document.getElementById('prevBtn').disabled = currentStep === 0;
            document.getElementById('nextBtn').disabled = currentStep === steps.length - 1;
        }

        // 高亮字节码
        function highlightBytecode() {
            // 移除之前的高亮
            document.querySelectorAll('.highlight').forEach(el => {
                el.classList.remove('highlight');
            });

            // 根据当前步骤高亮相应的字节码行
            const step = steps[currentStep];
            if (step.id >= 9 && step.id <= 16) {
                // main方法执行阶段
                highlightBytecodeLine('mainBytecodeContent', getBytecodeLineForMainStep(step.id));
            } else if (step.id >= 17 && step.id <= 25) {
                // add方法执行阶段
                highlightBytecodeLineForAddStep(step.id);
            }
        }

        // 高亮字节码行
        function highlightBytecodeLine(elementId, lineIndex) {
            const element = document.getElementById(elementId);
            if (element && lineIndex >= 0) {
                const lines = element.innerHTML.split('\n');
                if (lines[lineIndex]) {
                    lines[lineIndex] = '<span class="highlight">' + lines[lineIndex] + '</span>';
                    element.innerHTML = lines.join('\n');
                }
            }
        }

        // 获取main方法字节码行号
        function getBytecodeLineForMainStep(stepId) {
            const mapping = {
                10: 0, // new
                11: 1, // dup
                12: 2, // invokespecial
                13: 3, // astore_1
                14: 4, // aload_1
                15: 5, // invokevirtual
                16: 6  // pop
            };
            return mapping[stepId] || -1;
        }

        // 高亮add方法字节码
        function highlightBytecodeLineForAddStep(stepId) {
            const mapping = {
                18: 0, // iconst_1
                19: 1, // istore_1
                20: 2, // iconst_2
                21: 3, // istore_2
                22: [4, 5, 6], // iload_1, iload_2, iadd
                23: [7, 8], // bipush, imul
                24: 9, // istore_3
                25: [10, 11] // iload_3, ireturn
            };
            
            const lines = mapping[stepId];
            if (Array.isArray(lines)) {
                lines.forEach(line => highlightBytecodeLineInAdd(line));
            } else if (lines !== undefined) {
                highlightBytecodeLineInAdd(lines);
            }
        }

        function highlightBytecodeLineInAdd(lineIndex) {
            const element = document.getElementById('addBytecodeContent');
            if (element && lineIndex >= 0) {
                const lines = element.innerHTML.split('\n');
                if (lines[lineIndex]) {
                    lines[lineIndex] = '<span class="highlight">' + lines[lineIndex] + '</span>';
                    element.innerHTML = lines.join('\n');
                }
            }
        }

        // 更新内存状态
        function updateMemoryState() {
            updateHeapMemory();
            updateJvmStack();
            updateMethodArea();
            updatePCRegister();
        }

        // 更新堆内存
        function updateHeapMemory() {
            const heap = document.getElementById('heapMemory');
            const step = steps[currentStep];
            
            if (step.id >= 10 && step.id <= 25) {
                heap.innerHTML = `
                    <div class="heap-object">
                        <div><strong>JvmTest@0x1001</strong></div>
                        <div>Class: JvmTest.class</div>
                        <div>实例变量: 无</div>
                    </div>
                `;
            } else {
                heap.innerHTML = '<div>暂无对象</div>';
            }
        }

        // 更新JVM栈
        function updateJvmStack() {
            const stack = document.getElementById('jvmStack');
            const step = steps[currentStep];
            
            if (step.id >= 9 && step.id < 17) {
                // main方法栈帧
                stack.innerHTML = getMainStackFrame(step.id);
            } else if (step.id >= 17 && step.id <= 25) {
                // add方法栈帧 + main方法栈帧
                stack.innerHTML = getAddStackFrame(step.id) + getMainStackFrame(15);
            } else {
                stack.innerHTML = '<div>暂无栈帧</div>';
            }
        }

        // 获取main方法栈帧HTML
        function getMainStackFrame(stepId) {
            let operandStack = '[ ]';
            let localVars = `
                [0]: args[]<br>
                [1]: ${stepId >= 13 ? 'JvmTest@0x1001' : 'null'}
            `;

            if (stepId === 10 || stepId === 11) {
                operandStack = '[JvmTest@0x1001]';
            } else if (stepId === 14) {
                operandStack = '[JvmTest@0x1001]';
            }

            return `
                <div class="stack-frame">
                    <div><strong>main方法栈帧</strong></div>
                    <div class="operand-stack">操作数栈: ${operandStack}</div>
                    <div class="local-vars">局部变量表:<br>${localVars}</div>
                </div>
            `;
        }

        // 获取add方法栈帧HTML
        function getAddStackFrame(stepId) {
            let operandStack = '[ ]';
            let localVars = `
                [0]: JvmTest@0x1001<br>
                [1]: ${stepId >= 19 ? '1' : '未初始化'}<br>
                [2]: ${stepId >= 21 ? '2' : '未初始化'}<br>
                [3]: ${stepId >= 24 ? '30' : '未初始化'}
            `;

            // 根据步骤设置操作数栈内容
            switch(stepId) {
                case 18: operandStack = '[1]'; break;
                case 20: operandStack = '[2]'; break;
                case 22: operandStack = '[3]'; break;
                case 23: operandStack = '[30]'; break;
                case 25: operandStack = '[30]'; break;
            }

            return `
                <div class="stack-frame" style="border-color: #e74c3c;">
                    <div><strong>add方法栈帧</strong></div>
                    <div class="operand-stack">操作数栈: ${operandStack}</div>
                    <div class="local-vars">局部变量表:<br>${localVars}</div>
                </div>
            `;
        }

        // 更新方法区
        function updateMethodArea() {
            const methodArea = document.getElementById('methodArea');
            const step = steps[currentStep];
            
            if (step.id >= 4) {
                methodArea.innerHTML = `
                    <div>✅ JvmTest.class (已加载)</div>
                    <div style="margin-top: 5px; font-size: 12px; color: #bdc3c7;">
                        - add()方法字节码<br>
                        - main()方法字节码<br>
                        - 常量池
                    </div>
                `;
            } else {
                methodArea.innerHTML = '<div>JvmTest.class (待加载)</div>';
            }
        }

        // 更新程序计数器
        function updatePCRegister() {
            const pcRegister = document.getElementById('pcRegister');
            const step = steps[currentStep];
            
            let pc = 'N/A';
            if (step.id >= 9 && step.id < 17) {
                const pcMapping = { 10: 0, 11: 3, 12: 4, 13: 7, 14: 8, 15: 9, 16: 12 };
                pc = pcMapping[step.id] || 0;
            } else if (step.id >= 17 && step.id <= 25) {
                const pcMapping = { 
                    18: 0, 19: 1, 20: 2, 21: 3, 22: 4, 23: 7, 24: 10, 25: 11 
                };
                pc = pcMapping[step.id] || 0;
            }
            
            pcRegister.innerHTML = `<div>PC: ${pc}</div>`;
        }

        // 键盘事件
        document.addEventListener('keydown', function(e) {
            if (e.key === 'ArrowRight' || e.key === ' ') {
                e.preventDefault();
                nextStep();
            } else if (e.key === 'ArrowLeft') {
                e.preventDefault();
                prevStep();
            }
        });

        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', initPage);
    </script>
</body>
</html>
相关推荐
在下雨59914 小时前
优秀开源内容转自公众号后端开发成长指南
jvm
你我约定有三17 小时前
面试tips--JVM(4)--Minor GC & Major GC & Full GC
jvm·面试·职场和发展
Li_yizYa17 小时前
JVM:内存区域划分、类加载的过程、垃圾回收机制
java·jvm
A尘埃17 小时前
企业级架构师综合能力项目案例二(项目性能优化方案JVM+数据库+缓存+代码JUC+消息中间件架构+服务熔断降级)
jvm·数据库·性能优化·架构师
ByteBlossom17 小时前
JVM核心机制:类加载与内存结构详解
jvm
善我17 小时前
JVM中产生OOM(内存溢出)的8种典型情况及解决方案
jvm
程序员江鸟17 小时前
Java面试实战系列【JVM篇】- JVM内存结构与运行时数据区详解(共享区域)
java·jvm·面试
ybq1951334543118 小时前
RabbitMinQ(模拟实现消息队列项目)02
jvm
ChillJavaGuy2 天前
Java中的四大引用类型强引用、软引用、弱引用、虚引用
java·开发语言·jvm·四大引用类型