0

0

java 程序运行的基础知识

高洛峰

高洛峰

发布时间:2016-10-29 11:31:04

|

1995人浏览过

|

来源于php中文网

原创

JVM 线程栈 到 函数运行

每一个jvm线程来说启动的时候都会创建一个私有的线程栈。一个jvm线程栈用来存储栈帧,jvm线程栈和c语言中的栈很类似,它负责管理局部变量、部分运算结果,同时也参与到函数调用和函数返回的工作中。jvm规范中运行线程栈的大小可以是固定的或者是动态分配的,也可以是根据一定规则计算的。不同jvm对栈的实现会不同,一些可能提供给开发人员自己控制jvm线程栈初始大小的方式;对于动态分配来说也可能提供对jvm最大和最小值的设置。

当计算一个线程需要的分配的大小超出了固定值、或者设置的最大值,jvm会抛出StackOverflowError。而对于动态分配栈来说,如果内存不能够提供足够的空间来满足最小值、或者需要的值JVM会抛出 OutOfMemoryError

栈帧,可以理解成一个函数执行的环境,它管理参数、局部变量、返回值等等。

每个栈帧都包括一个管理局部变量的数组( local variables),这个数组的单元数量在编译成字节码的时候就能确定了。对于32-bit 一个单位能够存放 boolean, byte, char, short, int, float, reference,returnAddress;连续两个单位就能够用来存储long 、double。局部变量数组的下标是从0开始,一般而言0位置存储的是this,后面接着是函数的参数,再是函数中出现的局部变量。

每个栈帧也都包括一个(LIFO)操作栈的数据结构(operand stack),它的大小同样也可以在编译的时候确定,创建的时候会是个空栈。举个简单的例子,来描述它公用,对于int a+b来说,先把push a 进入栈中,再朴实 b 进入入栈中,然后 同时pop 两个值执行iadd 指令,再将其加后的结果push入栈中完成指令。

立即学习Java免费学习笔记(深入)”;

除开以上两个关键的结构,每个栈帧还有常量池( run-time constant pool)、异常抛出管理等结构。在此就不一一详细说来了,可以参考其他资料。

再来通过一个简单的 Demo 来说明,一个栈帧的工作。首先,我们来看这样的一个函数:

public int comp(float number1, float number2){
        int result ;
        if(number1 <p>其中函数内逻辑对应的字节码,如下:</p><p> 0: fload_1</p><p> 1: fload_2</p><p> 2: fcmpg</p><p> 3: ifge          11</p><p> 6: iconst_1</p><p> 7: istore_3</p><p> 8: goto          13</p><p>11: iconst_2</p><p>12: istore_3</p><p>13: iload_3</p><p>14: ireturn</p><p>对于这几个字节码指令稍微说明下:</p><p>fload_x:取局部变量数组中第x个,类型fload,push 入栈;</p><div class="aritcle_card flexRow">
                                                        <div class="artcardd flexRow">
                                                                <a class="aritcle_card_img" href="/xiazai/code/10539" title="Berlinet企业网站系统(绿色风格)1.0"><img
                                                                                src="https://img.php.cn/upload/webcode/000/000/020/176264640314486.jpg" alt="Berlinet企业网站系统(绿色风格)1.0"  onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
                                                                <div class="aritcle_card_info flexColumn">
                                                                        <a href="/xiazai/code/10539" title="Berlinet企业网站系统(绿色风格)1.0">Berlinet企业网站系统(绿色风格)1.0</a>
                                                                        <p>本程序是在DFCMS的基础上修正了一些调用和显示上的BUG,增加JS方式调用。  新闻,产品,留言,招聘。功能齐全,模板与程序分离。一般给中小企业客户 做网站,掌握该套程序足够。模板我会陆续提供。今天提供一套绿色风格的模 板,针对家居,运动,健身行业公司性质的模板。管理地址:manage/login.asp 管理账号和密码admin</p>
                                                                </div>
                                                                <a href="/xiazai/code/10539" title="Berlinet企业网站系统(绿色风格)1.0" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
                                                        </div>
                                                </div><p>fcmpg:比较两个单精度浮点数。如果两数大于结果为1,相等则结果为0,小于的话结果为-1;</p><p>ifge:跳转指令;</p><p>iconst_x:push 常量x入栈;</p><p>istore_x:pop栈存入局部变量数组第x个;</p><p>iload_x:读取局部变量数组第x个,入栈;</p><p>ireturn:函数结束返回int型;</p><p>细心点观察可以发现i开头指代int,f开头指代fload,load代表载入,if代表跳转等等,其中字节码的操作码定义也是有一定意义的,详情可以翻译jvm字节码相关标准。再来看看,jvm如何在栈帧结构上执行情况,以具体调用comp(1.02,2.02)为例:</p><p>@@##@@</p><p style="margin: 0px; padding: 0px; color: rgb(0, 0, 0); font-family: Verdana, Arial, Helvetica, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);">Java 的 Class</p><p>说字节码,一定少不了.class。不妨,以一个demo类 来具体看class 的内容,类非常简单,两个函数一个say,另外一个就是上面的cmp函数。</p><pre class="brush:java;toolbar:false">public class Hello {

    public void say(){
        System.out.println("Hello world!");
    }

    public int comp(float number1, float number2){
        int result ;
        if(number1 <p>用 javac -g:none Hello.java 来编译这个类的,然后用 javap -c -v Hello.class 来解析编译的class。</p><pre class="brush:java;toolbar:false">Classfile /src/main/java/com/demo/Hello.class
  Last modified 2016-10-28; size 404 bytes
  MD5 checksum 9ac6c800c312d65b568dd2a0718bd2c5public class com.demo.Hello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:   #1 = Methodref          #6.#14         // java/lang/Object."<init>":()V
   #2 = Fieldref           #15.#16        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #17            // Hello world!
   #4 = Methodref          #18.#19        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #20            // com/demo/Hello
   #6 = Class              #21            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               say
  #11 = Utf8               comp
  #12 = Utf8               (FF)I
  #13 = Utf8               StackMapTable
  #14 = NameAndType        #7:#8          // "<init>":()V
  #15 = Class              #22            // java/lang/System
  #16 = NameAndType        #23:#24        // out:Ljava/io/PrintStream;
  #17 = Utf8               Hello world!
  #18 = Class              #25            // java/io/PrintStream
  #19 = NameAndType        #26:#27        // println:(Ljava/lang/String;)V
  #20 = Utf8               com/demo/Hello
  #21 = Utf8               java/lang/Object
  #22 = Utf8               java/lang/System
  #23 = Utf8               out
  #24 = Utf8               Ljava/io/PrintStream;
  #25 = Utf8               java/io/PrintStream
  #26 = Utf8               println
  #27 = Utf8               (Ljava/lang/String;)V{  public com.demo.Hello();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:      stack=1, locals=1, args_size=1
         0: aload_0         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return

  public void say();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello world!
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return

  public int comp(float, float);
    descriptor: (FF)I
    flags: ACC_PUBLIC
    Code:      stack=2, locals=4, args_size=3
         0: fload_1         1: fload_2         2: fcmpg         3: ifge          11
         6: iconst_1         7: istore_3         8: goto          13
        11: iconst_2        12: istore_3        13: iload_3        14: ireturn
      StackMapTable: number_of_entries = 2
        frame_type = 11 /* same */
        frame_type = 252 /* append */
          offset_delta = 1
          locals = [ int ]
}</init></init></init></init>

解释下其中涉及的新的操作码

getstatic:获取镜头变量;
invokevirtual:调用函数;
return:void 函数结束返回;

在 public int comp(float, float) code 这段代码里面就能看到上面提到的字节码运行的例子。有了个感性认识,其实大体看到class文件里面,除了字节码指令外,还包括了常量pool,访问标志(public 等),类的相关信息(属性、函数、常量等)。因为前面用的是 -g:node进行编译的,其他模式下还可以有其他扩展、调试信息也包括在class里面。官方给出的class文件格式,详细如下:

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

magic: 就是非常有名的 0xCAFEBABE ,一个标识class文件;

minor_version 、major_version :指的是java class 文件的版本,一般说class文件的版本是 XX.xx 其中XX 就是major,xx是minor,比如上面demo中的版本是52.0 代表就是 minor 0,major 51.

constant_pool_count:就是常量池元素个数,cp_info constant_pool[constant_pool_count-1] 就是相关的详细信息了。

access_flags:指的是访问标识例如ACC_PUBLIC、ACC_FINAL、ACC_INTERFACE、ACC_SUPER 写过java的相信看名字应该知道啥意思,ACC是access的缩写。

其他具体的,就不一一介绍了详细可以直接参考官方文档。

动态生成java字节码

当然,你可以直接按照官方的class文件格式来直接写 byte[],然后自定义个 class load 载入编写的byte[]来实现动态生成class。不过,这个要求可能也有点高,必须的非常熟悉class文件格式才能做到。这里demo还是借助 ASM 这个类库来简单演示下,就编写下 上面的Hello 不过里面只实现say的方法。如下:

public class AsmDemo {    public static final String CLASS_NAME = "Hello";    
    public static final AsmDemoLoad load = new AsmDemoLoad();    private static class AsmDemoLoad extends ClassLoader {        public AsmDemoLoad() {            super(AsmDemo.class.getClassLoader());
        }        public Class> defineClassForName(String name, byte[] data) {            return this.defineClass(name, data, 0, data.length);
        }
    }    public static byte[] generateSayHello() throws IOException {

        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        classWriter.visit(V1_7, ACC_PUBLIC + ACC_SUPER, CLASS_NAME, null, getInternalName(Object.class), null);    
        //默认初始化函数
        Method constructorMethod = Method.getMethod("void <init> ()");
        GeneratorAdapter constructor = new GeneratorAdapter(ACC_PUBLIC, constructorMethod, null, null, classWriter);
        constructor.loadThis();        //每个类都要基础Object
        constructor.invokeConstructor(Type.getType(Object.class), constructorMethod);
        constructor.returnValue();
        constructor.endMethod();

        Method mainMethod = Method.getMethod("void say ()");
        GeneratorAdapter main = new GeneratorAdapter(ACC_PUBLIC, mainMethod, null, null, classWriter);
        main.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));
        main.push("Hello world!");
        main.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)"));
        main.returnValue();
        main.endMethod();        return classWriter.toByteArray();
    }    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException,
            InvocationTargetException, InstantiationException, NoSuchMethodException, SecurityException, IOException {        byte[] code = AsmDemo.generateSayHello();        //反射构建 hello 类,调用hello方法。
        Class> hello = load.defineClassForName(CLASS_NAME, code);
        hello.getMethod("say", null).invoke(hello.newInstance(), null);
    }
}</init>

关于动态生成字节码用途,一定场景下是可以提升效率与性能,因为动态生成的类和普通的载入类并无太大区别。手工优化后的字节码执行可能比编译的要优,可以替代反射使用的许多场景 同时避免反射的性能消耗。很著名的一个例子,fastJSON 就是使用内嵌 ASM 框架动态生成字节码类,来进行序列和反序列化工作,是目前公认最快的json字符串解析。

1.png
java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

43

2026.02.28

Golang 工程化架构设计:可维护与可演进系统构建
Golang 工程化架构设计:可维护与可演进系统构建

Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

38

2026.02.28

Golang 性能分析与运行时机制:构建高性能程序
Golang 性能分析与运行时机制:构建高性能程序

Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

35

2026.02.28

Golang 并发编程模型与工程实践:从语言特性到系统性能
Golang 并发编程模型与工程实践:从语言特性到系统性能

本专题系统讲解 Golang 并发编程模型,从语言级特性出发,深入理解 goroutine、channel 与调度机制。结合工程实践,分析并发设计模式、性能瓶颈与资源控制策略,帮助将并发能力有效转化为稳定、可扩展的系统性能优势。

20

2026.02.27

Golang 高级特性与最佳实践:提升代码艺术
Golang 高级特性与最佳实践:提升代码艺术

本专题深入剖析 Golang 的高级特性与工程级最佳实践,涵盖并发模型、内存管理、接口设计与错误处理策略。通过真实场景与代码对比,引导从“可运行”走向“高质量”,帮助构建高性能、可扩展、易维护的优雅 Go 代码体系。

18

2026.02.27

Golang 测试与调试专题:确保代码可靠性
Golang 测试与调试专题:确保代码可靠性

本专题聚焦 Golang 的测试与调试体系,系统讲解单元测试、表驱动测试、基准测试与覆盖率分析方法,并深入剖析调试工具与常见问题定位思路。通过实践示例,引导建立可验证、可回归的工程习惯,从而持续提升代码可靠性与可维护性。

3

2026.02.27

漫蛙app官网链接入口
漫蛙app官网链接入口

漫蛙App官网提供多条稳定入口,包括 https://manwa.me、https

235

2026.02.27

deepseek在线提问
deepseek在线提问

本合集汇总了DeepSeek在线提问技巧与免登录使用入口,助你快速上手AI对话、写作、分析等功能。阅读专题下面的文章了解更多详细内容。

11

2026.02.27

AO3官网直接进入
AO3官网直接进入

AO3官网最新入口合集,汇总2026年可用官方及镜像链接,助你快速稳定访问Archive of Our Own平台。阅读专题下面的文章了解更多详细内容。

382

2026.02.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4万人学习

C# 教程
C# 教程

共94课时 | 10.5万人学习

Java 教程
Java 教程

共578课时 | 75.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号