【Java进阶营】Java程序运行原理分析

本文详细介绍了JVM的运行时数据区,包括类文件内容、堆、虚拟机栈、本地方法栈、程序计数器和方法区的工作原理。通过Demo.java的编译和分析,展示了Java类文件的结构以及程序执行的过程,帮助理解JVM如何加载和执行代码。
摘要由CSDN通过智能技术生成

一. JVM原理

class文件内容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cEnCgkku-1650627075591)(//upload-images.jianshu.io/upload_images/14004247-2cd494127435b211.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)]

JVM运行时数据区

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FUkQVnRl-1650627075594)(//upload-images.jianshu.io/upload_images/14004247-eccfcc2b31c4f14f.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)]

线程独占: 每个线程都会有它独立的空间,随线程的生命周而创建和销毁

线程共享: 所有线程都能访问这块内存数据,随虚拟机或GC而创建和销毁

方法区
  • 方法区是各个线程共享的内存区域

  • 用于存储已被虚拟机加载的类信息, 常量,静态变量, 即时编译后的代码等数据

  • 虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分, 但它却有一个别名叫Non-Heap, 目的应该是与Java堆区分开来

  • Oracle的Hotspot虚拟机在Java7中方法区放在’永久代’(Permanent Generation), Java8放在元数据空间, 并且通过GC机制对这个区域进行管理

  • 运行时常量池是方法区的一部分

Java堆
  • Java堆是被所有共享的一块内存区域, 在虚拟机启动时创建

  • 存放对象的实例

  • 垃圾收集器的主要管理区域

  • Java堆还可以细分为: 新生代和老年代, 新生代又可以细分为Eden 空间, From Survivor空间 和To Survivor空间

  • 空间满了会抛OutOfMemoryError

Java虚拟机栈
  • Java虚拟机栈是线程私有的, 它的生命周期与线程相同

  • Java虚拟机栈描述的是Java方法执行的内存模型: 每个方法被执行的的时候都会同时创建一个栈帧(栈帧是方法运行时的基础数据结构)用于存储局部变量表, 操作栈, 动态链接, 方法出口等信息.在此我向大家推荐一个架构学习交流圈。交流学习伪鑫:1253431195(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

  • 栈内存默认最大是1M, 超出则抛出StackOverFlowError

本地方法栈
  • 本地方法栈与虚拟机栈的功能类似, 虚拟机栈是为虚拟机执行Java方法而准备的, 本地方法栈是为虚拟机使用Native本地方法而准备的

  • Hotspot虚拟机中虚拟机栈与本地方法栈的实现方式一样, 超出大小后也会抛StackOverFlowError

程序计数器
  • 程序计数器是线程私有的一块较小的内存空间

  • 记录当前线程执行的字节码位置, 存储的是字节码指令地址, 如果执行Native方法, 则计数器为空

  • CPU同一时间, 只会执行一条线程的指令. JVM多线程会轮流切换并分配CPU的执行时间的方式. 为了线程切换后, 需要通过程序计数器来恢复正确的执行位置

二. 程序运行分析

使用Demo.java进行测试, 运行javac Demo.java编译成class文件, 然后运行javap -v Demo.class > Demo.txt查看class文件内容

public class Demo{
    public static void main(String[] args){
        int x = 500;
        int y = 100;
        int a = x / y;
        int b = 50;
        System.out.println(a + b);
    }
}

demo.txt

Classfile /E:/*/Demo.class
  Last modified 2019-6-30; size 412 bytes
  MD5 checksum efd785af33e58aa9fc9834110b74b87b
  Compiled from "Demo.java"
public class Demo
  minor version: 0      //次版本号
  major version: 52     //主版本号 版本号规则: JDK5,6,7,8分别对应49,50,51,52
  flags: ACC_PUBLIC, ACC_SUPER      //访问标志
Constant pool:      // 常量池 类信息包含的静态常量, 编译之后就能确认
   #1 = Methodref          #5.#14         // java/lang/Object."<init>":()V
   #2 = Fieldref           #15.#16        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #17.#18        // java/io/PrintStream.println:(I)V
   #4 = Class              #19            // Demo
   #5 = Class              #20            // 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               SourceFile
  #13 = Utf8               Demo.java
  #14 = NameAndType        #6:#7          // "<init>":()V
  #15 = Class              #21            // java/lang/System
  #16 = NameAndType        #22:#23        // out:Ljava/io/PrintStream;
  #17 = Class              #24            // java/io/PrintStream
  #18 = NameAndType        #25:#26        // println:(I)V
  #19 = Utf8               Demo
  #20 = Utf8               java/lang/Object
  #21 = Utf8               java/lang/System
  #22 = Utf8               out
  #23 = Utf8               Ljava/io/PrintStream;
  #24 = Utf8               java/io/PrintStream
  #25 = Utf8               println
  #26 = Utf8               (I)V
{
  public Demo();     // 默认隐式无参的构造函数
    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
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);      //程序的入口main方法
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC       //访问控制
    Code:
      stack=3, locals=5, args_size=1        //方法栈栈帧中操作数栈的深度,本地变量数量,参数数量
         0: sipush        500       //Jvm执行引擎执行这些源码编译过后的指令码, javap翻译出来的
         3: istore_1                //是操作符, class 文件内存储的是指令码, 前面的数据是偏移量,
         4: bipush        100       //Jvm根据这个去区分不同的指令. 详情参照'JVM指令码表'
         6: istore_2
         7: iload_1
         8: iload_2
         9: idiv
        10: istore_3
        11: bipush        50
        13: istore        4
        15: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        18: iload_3
        19: iload         4
        21: iadd
        22: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        25: return
      LineNumberTable:
        line 3: 0
        line 4: 4
        line 5: 7
        line 6: 11
        line 7: 15
        line 8: 25
}
SourceFile: "Demo.java"

Class执行过程详解

程序分析图1 - 加载到方法区

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4gT5KBtt-1650627075596)(//upload-images.jianshu.io/upload_images/14004247-58b5a8a2d86aaa27.PNG?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)]

程序分析图2 - JVM创建线程执行代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FDKk3dPe-1650627075608)(//upload-images.jianshu.io/upload_images/14004247-45ae6cb4389c425a.PNG?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)]

程序分析图3 - main方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NgeJ0Aez-1650627075610)(//upload-images.jianshu.io/upload_images/14004247-c12932a674c35e15.gif?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值