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 < number2)
            result = 1;
        else
            result = 2;
        return result;
    }

其中函数内逻辑对应的字节码,如下:

 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

对于这几个字节码指令稍微说明下:

fload_x:取局部变量数组中第x个,类型fload,push 入栈;

Berlinet企业网站系统(绿色风格)1.0
Berlinet企业网站系统(绿色风格)1.0

本程序是在DFCMS的基础上修正了一些调用和显示上的BUG,增加JS方式调用。 新闻,产品,留言,招聘。功能齐全,模板与程序分离。一般给中小企业客户 做网站,掌握该套程序足够。模板我会陆续提供。今天提供一套绿色风格的模 板,针对家居,运动,健身行业公司性质的模板。管理地址:manage/login.asp 管理账号和密码admin

下载

fcmpg:比较两个单精度浮点数。如果两数大于结果为1,相等则结果为0,小于的话结果为-1;

ifge:跳转指令;

iconst_x:push 常量x入栈;

istore_x:pop栈存入局部变量数组第x个;

iload_x:读取局部变量数组第x个,入栈;

ireturn:函数结束返回int型;

细心点观察可以发现i开头指代int,f开头指代fload,load代表载入,if代表跳转等等,其中字节码的操作码定义也是有一定意义的,详情可以翻译jvm字节码相关标准。再来看看,jvm如何在栈帧结构上执行情况,以具体调用comp(1.02,2.02)为例:

1.png

Java 的 Class

说字节码,一定少不了.class。不妨,以一个demo类 来具体看class 的内容,类非常简单,两个函数一个say,另外一个就是上面的cmp函数。

public class Hello {

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

    public int comp(float number1, float number2){
        int result ;
        if(number1 < number2)
            result = 1;
        else
            result = 2;
        return result;
    }
}

javac -g:none Hello.java 来编译这个类的,然后用 javap -c -v Hello.class 来解析编译的class。

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."":()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               
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               say
  #11 = Utf8               comp
  #12 = Utf8               (FF)I
  #13 = Utf8               StackMapTable
  #14 = NameAndType        #7:#8          // "":()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."":()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 ]
}

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

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  ()");
        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);
    }
}

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

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

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

下载

相关标签:

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

相关专题

更多
Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

37

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

37

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

16

2026.01.13

PHP缓存策略教程大全
PHP缓存策略教程大全

本专题整合了PHP缓存相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

jQuery 正则表达式相关教程
jQuery 正则表达式相关教程

本专题整合了jQuery正则表达式相关教程大全,阅读专题下面的文章了解更多详细内容。

3

2026.01.13

交互式图表和动态图表教程汇总
交互式图表和动态图表教程汇总

本专题整合了交互式图表和动态图表的相关内容,阅读专题下面的文章了解更多详细内容。

45

2026.01.13

nginx配置文件详细教程
nginx配置文件详细教程

本专题整合了nginx配置文件相关教程详细汇总,阅读专题下面的文章了解更多详细内容。

9

2026.01.13

热门下载

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

精品课程

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

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.7万人学习

Java 教程
Java 教程

共578课时 | 46.1万人学习

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

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