本讲将介绍Java代码是如何一步步运行起来的,其中涉及的编译器,类加载器,字节码校验器,解释器和JIT编译器在整个过程中是发挥着怎样的作用。此外还会介绍Java程序所占用的内存是被如何管理的:堆、栈和方法区都各自负责存储哪些内容。最后用一小块代码示例来帮助理解Java程序运行时内存的变化。

Java程序执行过程

  • 步骤 1: 写源代码,源代码将以.java的文件格式保存在电脑硬盘中。
  • 步骤 2: 编译器(compiler)检查是否存在编译期错误(例如缺少分号,关键字拼写错误等)。若通过检测,编译器就会将源代码翻译成字节码(bytecode),以.class的文件格式保存在电脑硬盘中。
  • 步骤 3: 若要运行此Java程序,JVM中会有一个叫类加载器(class loader)的内置程序把字节码从硬盘载入到正位于内存中的JVM里去。
  • 步骤 4: JVM中还有一个叫字节码校验器(bytecode verifier)的内置程序检测是否存在运行期错误(例如栈溢出)。若通过检测,字节码校验器就会将字节码传递给解释器(interpreter)。
  • 步骤 5: 解释器会对字节码进行逐行翻译,将其翻译成当前所在系统可以理解的机器码(machine code),
  • 步骤 6:将机器码交给操作系统,操作系统会以main方法作为入口开始执行程序。至此,一个Java程序就这样运行起来了。

细心的读者可能注意到了,在流程图中还涉及到一个叫JIT的东西在步骤中没有被解释。那么JIT编译器(Just-In-Time Compiler)是如果参与进程序的执行过程中呢?让我们来看以下两个例子。

  • 情况 1: 解释器对代码进行逐行解释,正如我们在步骤中所介绍的。
  • 情况 2: 这是JIT编译器参与进Java执行过程的情况,JIT编译器会扫描所有代码并对其进行优化。例如此时它发现最后一行代码是重复多余的,就会将其移除,只传递前4行代码给解释器。这样解释器就能运行地更快速高效,毕竟少了一行多余的代码需要翻译。

当然,这只是JIT编译器的优化手段之一,不同公司设计的JIT编译器对Java程序的运行会有不同的优化方式。此外需要知道的是,JIT编译器并不是每次都会参与到执行过程中来。

内存机制

在步骤3中我们谈到字节码会被类加载器载入到内存,那么载入之后JVM是如何对其进行内存管理的呢?

通常,在载入内存后,一个Java程序所占用的内存会被大致分为3块区域:堆(heap),栈(stack)和方法区(method area)。

堆:存放new出来的东西。

栈:存放局部变量。

方法区:类型信息,字段信息,常量池(constant pool),静态变量,方法信息等。

public final class Student extends Object implements Serializable { // 1.类信息
// 2.对象字段信息
private String name;
private int score; // 3.常量池
public final int id = 0;
public final String gender = "male"; // 4.静态变量
public static int a = 0; // 5.方法信息
public int getid() {
return id;
}
}

PC寄存器:存放将要执行的指令的地址。(因为机器的脑子不灵活,所以需要一块专门的区域帮他记住执行到哪一步,不然它会忘记)

本地方法栈:与JVM栈所发挥的作用是非常相似的,其区别不过是JVM栈为Java方法服务,而本地方法栈则是为使用到的Native方法服务。有的虚拟机(例如Sun HotSpot虚拟机)甚至直接就把本地方法栈和虚拟机栈合二为一。

每个线程拥有各自独立的(虚拟机)栈、PC寄存器和本地方法栈。而堆和方法区则是所有线程共享的。

最后让我们通过一个小例子来理解Java程序执行时内存的变化。

public class Person {
int id;
int age; Person(int id1, int age1) {
id = id1;
age = age1;
} public static void main(String[] args) {
Person Tom = new Person(1, 25);
}
}

首先,在stack中申请了一块内存,这块内存区域名字叫Tom,此时区域里存储的内容为null。

接着,调用Person的构造方法,方法的参数属于局部变量,因此在stack中有两块区域分别存放id1和age1。

通过构造方法,可以new出来一个Person的对象,这个对象连带着其成员变量会被存放在heap中。成员变量id和age的值由存放在stack中的局部变量id1和age1赋予。

最后,将这个对象的引用值(类似于地址)传递给Tom,通过引用值我们就可以找到这个对象。

(注意:位于stack中的id1和age1会随着构造方法调用的结束而消失,这里为了更好地表现全过程,因此保留在图中。)

关于栈和堆的其他小知识

  • 栈和堆的大小都是固定为一个默认值的,它们作为jvm的参数设定好了,不同的jvm设定的参数不同,相应的栈和堆的大小也就不同。
  • 栈是运行时的单位:里面存储的信息都是跟当前线程相关的,包括局部变量、程序运行状态、方法返回值等;而堆是存储的单位:它只负责存储对象。
  • 当一个方法调用结束后,方法里的局部变量会随之消失,不会在stack中继续占用空间。
  • 栈与堆的分离使得不同线程可以访问同一个对象,这是一种有效的数据交互方式(共享内存);此外也节省了空间,因为堆中的共享常量和缓存可以被所有栈访问。

参考

  1. https://simplesnippets.tech/execution-process-of-java-program-in-detail-working-of-just-it-time-compiler-jit-in-detail/
  2. https://blog.csdn.net/yfqnihao/article/details/8289363
  3. https://www.zhihu.com/question/29833675

有问题欢迎大家在评论区留言,转载请注明出处。

Java程序执行过程及内存机制的更多相关文章

  1. 【JAVA】【基础知识】Java程序执行过程

    1. Java程序制作过程 使用文本编辑器进行编辑 2. 编译源文件,生成class文件(字节码文件) javac源文件路径. 3.运行程序class文件.

  2. Java程序执行过程

    首先,写好Java代码,保存到硬盘中.然后在命令行中输入: javac ClassName.java 此时,这个Java类文件将编译成字节码(.class)文件.如果用Eclipse等IDE开发工具, ...

  3. 性能监控之Java程序执行解析

    大家好,最近接触javassist技术,研究过程中对Java程序执行过程进行了一系列探索,弄清楚了几个盲区(仅针对个人而言),现将经验与大家分享. 1.编码->.java 通常指写代码的过程,最 ...

  4. Java性能监控之Java程序执行解析

    大家好,最近接触javassist技术,研究过程中对Java程序执行过程进行了一系列探索,弄清楚了几个盲区(仅针对个人而言),现将经验与大家分享. 1.编码->.java 通常指写代码的过程,最 ...

  5. Android入门学习:Android 系统框架及应用程序执行过程

    Android基础知识学习 新手上路,还请多多帮助.由于初学,博客内容难免有不正确的地方,还请各位多多指教,相互学习! 主要内容: 1.Android层次架构及主要功能 2.Android编程模型,程 ...

  6. Java程序语言的后门-反射机制

    在文章JAVA设计模式-动态代理(Proxy)示例及说明和JAVA设计模式-动态代理(Proxy)源码分析都提到了反射这个概念. // 通过反射机制,通知力宏做事情 method.invoke(obj ...

  7. java 程序运行过程 简介

    这里的Java程序运行过程,是指我们编译好代码之后,在命令行开始执行java xxx命令,到java程序开始执行起来的这一过程,我们称其为运行时. 第一步,操作系统解析我们输入的java xxx命令, ...

  8. Java基础知识强化之网络编程笔记25:Android网络通信之 Future接口介绍(Java程序执行超时)

    1. Future接口简介 在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现. Future接口是Java标准API ...

  9. Windows程序执行过程

    Windows应用程序: WinMain函数(入口函数): 1. 设计窗体类,注冊窗体类.WNDCLASS 2. 创建窗体,显示及更新窗体. 3. 消息循环. 窗体过程函数(回调函数):WindowP ...

随机推荐

  1. 深入理解h2和r2dbc-h2

    简介 本文将会介绍R2DBC的H2实现r2dbc-h2的使用方法和要注意的事项.一起来看看吧. H2数据库简介 什么是H2数据库呢? H2是一个Java SQL database,它是一个开源的数据库 ...

  2. SQL Server DATEDIFF() 函数用法

    定义和用法 DATEDIFF() 函数返回两个日期之间的时间,例如计算年龄大小. DATEDIFF(datepart,startdate,enddate)startdate 和 enddate 参数是 ...

  3. ServiceStack.Redis 的 ASP.NET Core 扩展库

    给大家安利一款 ServiceStack.Redis 的 ASP.NET Core 扩展库,它是基于 ServiceStack.Redis.Core 开发的. 简单易用,开源免费,使用ASP.NET ...

  4. Weblogic CVE-2020-2551漏洞复现&CS实战利用

    Weblogic CVE-2020-2551漏洞复现 Weblogic IIOP 反序列化 漏洞原理 https://www.anquanke.com/post/id/199227#h3-7 http ...

  5. 基于物联网的O2O养猪平台

    引言:随着生活水平的提高,人们会越来越重视食品安全问题.在城市里的人想养头猪,并想看着它快快乐乐长大.但是无奈于自己没时间和精力.而农户想养猪,可能又缺少启动资金,且不能承担大的风险.这时候我的美团式 ...

  6. Guitar Pro 7 中文界面的介绍

    用过Guitar Pro这款软件的小伙伴们都知道,Guitar Pro这款吉他软件因为是国外开发商研发的,所以软件最初都是英文版本,对于国内的的吉他爱好者来说,在软件使用上还是很不方便的.随着Guit ...

  7. 免费在线使用Ayoa:让思维导图更简单

    在学习和工作中,我们都会遇到需要使用思维导图的情况,导图可以很好地帮助我们处理工作,完成记录和分享,是一个非常便捷的工具.今天小编就想和大家分享一款非常简单好用的思维导图软件,Ayoa. 之所以要将它 ...

  8. 你还在 if...else?代码这样写才好看!

    前言 if...else 是所有高级编程语言都有的必备功能.但现实中的代码往往存在着过多的 if...else.虽然 if...else 是必须的,但滥用 if...else 会对代码的可读性.可维护 ...

  9. 【移动自动化】【二】Appium

    实施自动化需要的工具 adb Android控制工具,获取获取Android各种数据和控制,Appium会调起adb命令去执行Android设备 adb命令参考 https://www.cnblogs ...

  10. So Easy! HDU - 4565

    易知,有\(S_n = \lceil{a + \sqrt{b}}\rceil ^ n\) \(\because a ^ 2 - 1 < b < a ^ 2\) \(\therefore a ...