用字节码分析Java的For循环

Fou循环常常用,但是在字节码层它是怎样执行的呢?出于兴趣驱使,就有了这篇短文了!

首先要分析字节码就得先写个类了,代码如下:

public class ForTest{
	public static void main(String[] args) {
		for (int i = 0; i < 10; ++i) {
			System.out.println(i);
		}
	}
}

简单的for循环打印10个数,那么编译之后就该得到他的字节码了,使用命令

javap -v ForTest.class > ForTest.j

这里用到 -v 参数,导出了整个class文件的详情,如果只是想简单得到字节码,就只需换成 -c 参数就成了,这里得到的结果如下:

Classfile /E:/Develop/JavaTest/ForTest.class
  Last modified 2015-1-2; size 437 bytes
  MD5 checksum 21aad18f3a51ffa226ad2f49d7c3f5e0
  Compiled from "ForTest.java"
public class ForTest
  SourceFile: "ForTest.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER

Constant pool:
   #1 = Methodref          #5.#15         //  java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        //  java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #18.#19        //  java/io/PrintStream.println:(I)V
   #4 = Class              #20            //  ForTest
   #5 = Class              #21            //  java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               main
  #11 = Utf8               ([Ljava/lang/String;)V
  #12 = Utf8               StackMapTable
  #13 = Utf8               SourceFile
  #14 = Utf8               ForTest.java
  #15 = NameAndType        #6:#7          //  "<init>":()V
  #16 = Class              #22            //  java/lang/System
  #17 = NameAndType        #23:#24        //  out:Ljava/io/PrintStream;
  #18 = Class              #25            //  java/io/PrintStream
  #19 = NameAndType        #26:#27        //  println:(I)V
  #20 = Utf8               ForTest
  #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               (I)V
{
  public ForTest();
    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        
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC

    Code:
      stack=2, locals=2, args_size=1
         0: iconst_0      
         1: istore_1      
         2: iload_1       
         3: bipush        10
         5: if_icmpge     21
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: iload_1       
        12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        15: iinc          1, 1
        18: goto          2
        21: return        
      LineNumberTable:
        line 3: 0
        line 4: 8
        line 3: 15
        line 6: 21
      StackMapTable: number_of_entries = 2
           frame_type = 252 /* append */
             offset_delta = 2
        locals = [ int ]
           frame_type = 250 /* chop */
          offset_delta = 18

}

其实重点还是下面这段代码,其他内容只是方便理解和分析class文件:

         0: iconst_0      
         1: istore_1      
         2: iload_1       
         3: bipush        10
         5: if_icmpge     21
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: iload_1       
        12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        15: iinc          1, 1
        18: goto          2
        21: return

现在就分析分析For循环是怎么执行的了

首先先初始化循环变量:

         0: iconst_0      
         1: istore_1

这两行代码相当于   int i = 0  这句代码(iconst_0 是数字 0,istore_1 就是表示局部变量1,这里就是源码里的 i 了)

然后判断循环条件:

         2: iload_1       
         3: bipush        10
         5: if_icmpge     21

这三行意思就是 i 是否小于 10 ,小于则继续往下执行,否则就跳到 编号为 21 的 return那里也即跳出for循环了(iload_1 就是指读取局部变量1 即 源码里的 i)

条件判断成立了那么就执行For循环体了:

         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: iload_1       
        12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V

这三行结果就是执行了 System.out.println(i); 这句代码

这里的 #2 和 #3 代表的意思可以在 常数池 里找到了,所以在使用 javap命令的时候我用了 -v 参数来方便理解了

既然循环体执行完了,那么接着就得更新循环变量了:

        15: iinc          1, 1

即 ++i; 语句

最后就是跳到判断语句的地方了:

        18: goto          2


For循环的简单分析就这样了,有误或不妥处欢迎指正

时间: 2024-10-11 07:00:12

用字节码分析Java的For循环的相关文章

从字节码看Java中for-each循环(增强for循环)实现原理

转发:http://blog.csdn.net/u011392897/article/details/54562596 for-each循环是jdk1.5引入的新的语法功能.并不是所有东西都可以使用这个循环的.可以看下Iterable接口的注释,它说明了除了数组外,其他类想要使用for-each循环必须实现这个接口.这一点表明除了数组外的for-each可能底层是由迭代器实现的. Iterable接口在1.8之前只有一个方法,Iterator<T> iterator(),此方法返回一个迭代器.

通过字节码分析java中的switch语句

在一次做题中遇到了switch的问题,由于对switch执行顺序的不了解,在这里简单的通过字节码的方式理解一下switch执行顺序(题目如下): public class Ag{ static public int i=10; public static void main(String []args){ switch(i){ default: System.out.println("this is default"); case 1: System.out.println("

透过字节码分析java基本类型数组的内存分配方式。

我们知道java中new方式创建的对象都是在堆中创建的,而局部变量对应的值存放在栈上.那么java中的int [] arr={1,2,3}是存放在什么地方的呢,int []arr = new int[3]又是存放在什么地方的呢, 下面我们通过编写两个小例子,通过查看生成的字节码文件,来了解jvm会如何来处理这两种情况的. 1.int[] arr = new int[3]示例 public class ArrayTest { public static void main(String[] arg

透过字节码分析Java动态代理机制。

一.创建动态代理代码 1.创建接口 public interface Subject { void request(); } 2.创建接口实现类 public class RealSubject implements Subject { @Override public void request() { System.out.println("From real subject"); } } 3.创建DynamicSubject类 public class DynamicSubject

Java并发编程原理与实战八:产生线程安全性问题原因(javap字节码分析)

前面我们说到多线程带来的风险,其中一个很重要的就是安全性,因为其重要性因此,放到本章来进行讲解,那么线程安全性问题产生的原因,我们这节将从底层字节码来进行分析. 一.问题引出 先看一段代码 package com.roocon.thread.t3; public class Sequence { private int value; public int getNext(){ return value++; } public static void main(String[] args) { S

JVM-String比较-字节码分析

一道String字符串比较问题引发的字节码分析 public class a { public static void main(String[] args)throws Exception{ } public static void aa(){ String s1="a";//内存在方法区的常量池 String s2="b";//内存在方法区的常量池 String s12 = "ab";//内存在方法区的常量池 String s3 = s1 +

jQuery源码分析-jQuery中的循环技巧

Js代码   作者:nuysoft/JS攻城师/高云 QQ:47214707 EMail:[email protected] 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 前记:本文收集了jQuery中出现的各种遍历技巧和场景 Js代码   // 简单的for-in(事件) for ( type in events ) { } Js代码   // 缓存length属性,避免每次都去查找length属性,稍微提升遍历速度 // 但是如果遍历HTMLCollection时,性能提升非常

java中i=i++字节码分析

原文出处: Ticmy 1 2 int i = 0; i = i++; 结果还是0为什么? 程序的执行顺序是这样的:因为++在后面,所以先使用i,"使用"的含义就是i++这个表达式的值是0,但是并没有做赋值操作,它在整个语句的最后才做赋值,也就是说在做了++操作后再赋值的,所以最终结果还是0 让我们看的更清晰点: 1 2 int i = 0;//这个没什么说的 i = i++;//等效于下面的语句: 1 2 3 int temp = i;//这个temp就是i++这个表达式的值 i++

通过字节码分析JDK8中Lambda表达式编译及执行机制

关于Lambda字节码相关的文章,很早之前就想写了,[蜂潮运动]APP 产品的后端技术,能快速迭代,除了得益于整体微服架构之外,语言层面上,也是通过Java8的lambda表达式的运用以及rxJava响应式编程框架,使代码更加简洁易维护,调用方式更加便捷.本文将介绍JVM中的方法调用相关的字节码指令,重点解析JDK7(JSR-292)之后新增的invokedynamic指令给lambda表达式的动态调用特性提供的实现机制,最后再探讨一下lambda性能方面的话题. 方法调用的字节码指令 在介绍i