JVM内存分析,以及一步步详解Java程序是如何运行的?

简介: 云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! VM也就是Java虚拟机,它的内存结构这块知识点。 你说它重要吧,编写代码基本用不到它;你说它不重要吧,程序员想要进阶又必须对底层有一定的了解。

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


VM也就是Java虚拟机,它的内存结构这块知识点。

你说它重要吧,编写代码基本用不到它;

你说它不重要吧,程序员想要进阶又必须对底层有一定的了解。

最终还是决定更加深入地学习下JVM,同时也用自己的理解详细地说明Java程序是如何运行的。

当然本人目前的能力有限,只能说尽己之能学的越多越好。

一、Java程序开发三步骤

编写、编译和运行,图解如下:

1

①编写阶段

后缀名为.Java的文件,也就是所谓的源码。

但是Java虚拟机它并不认识.Java文件,Java虚拟机和Java语言其实并没有必然的联系。

其实我挺想吐槽的,你们长的这么像,竟然不认识?

那为何不认识呢?

按照我个人的理解,我们常说的代码、Java语言,其实本质上还是人类定义的一门语言,主要由英文组成,Java虚拟机 本身并不认识它。

所以需要将其编译成Java虚拟机 认识的语言,即.class文件。

②编译阶段

后缀名为.class的文件,也就是所谓的字节码文件。

字节码文件,就可以理解成我们写代码一个类(接口、枚举、注释)里面的所有数据。

.class文件是如何来的?

javac编译器编译而来的,它能将.java文件编译成.class文件,这样的话JVM也就能认识.class文件了。

我们平时写的代码其实也是Java源码,只不过开发工具很强大,将这三个阶段糅合在一起了,以IDEA工具为例:

2

src,其实就是源码的意思,我们平时编写的类、接口、枚举、注解等文件,其实本质上就是.java文件。

利用开发工具中的Show in Explorer功能,可以找到计算机里对应的文件夹文件。

out,这个我还不太懂具体是什么意思,但是.class文件就在这个文件夹里面。

如果不用开发工具,我们需要用javac编译器将.java文件编译成.class文件;

开发工具等于是自动帮我们编译了,非常的方便,但是原理我们要明白。

既然讲到了.class文件,再回顾下反射中刚学的Class对象:

Class本身也是Java里的一个类,它其实就是指的字节码文件。

注意:小写字母开头的class是Java里的一个关键字,创建一个类时类名前面都会由它来修饰。

所以Class类也就表示成:class Class{};

前面是关键字,后面是类名。

最后用一个例子来说明:

我们在开发工具里面编写一个Student类;

那么在对应的文件夹下就会有一个Student.java文件,也就是源码;同时开发工具会给我编译一个对应的Student.class文件,也就是字节码文件;

而这个字节码文件会有唯一的一个Class对象,专门用来描述该字节码文件,在反射中可以拿来使用,除此之外还可以作为线程锁。

③运行阶段

Java虚拟机是认识.class文件的,也就是说能够用来运行程序了。

但它具体是如何运行的呢?

就需要知道JDK了,图解如下:

2

①JRE:Java Runtime Environment

翻译过来就是Java运行环境,包含JVM和运行时所需要的核心类库。

也就是说有了JRE就可以运行Java程序了,但是只能用来运行,如果出了bug,是没法修改的,所以需要JDK。

②JDK:Java Development Kit

翻译过来就是Java程序开发工具包,包含JRE 和开发人员使用的工具。

前面提到的javac编译器就是Java工具,JRE中是没有javac编译器的,所以它没法修改程序,毕竟没有编译的话JVM是不认识的。

Java虚拟机是认识字节码文件的,并且本质上它就是一个字节码翻译器。

它可以将字节码文件翻译成各个系统(Windows系统、Mac系统、linux系统)对应的机器码;

这样的话就能确保字节码文件能在各个系统上正确运行。

这也就是Java所谓的跨平台特性的由来。

接下来我们就详细地了解下Java虚拟机。

二、JVM内存结构

我们看oracle最新的官方文档,最权威的便是这个官方文档,毕竟技术是不断更新的:

3

JVM内存主要分为这五大块:

  • 程序计数器(The pc Register)
  • Java虚拟机栈(Stacks)
  • 堆内存(Heap)
  • 方法区(Method Area)
  • 本地方法栈(Native Method Stacks)

其中官方文档中还有一个运行时常量池,暂且不考虑。

①stack栈

我们常说的栈内存,其实严格上来说应该叫Java虚拟机栈。

利用开发工具IDEA断点调试,我们可以很清晰地看到方法进栈出栈的过程。

5

这是一段非常简易的代码:main方法里面调用method1方法,method1方法里面调用method2方法。

利用右上角两个功能键,可以一步一步了解方法进栈出栈的过程。

Frames,翻译过来就是帧的意思,一个方法对应一个栈帧。

什么叫帧?

在影像动画里 ,一个镜头就是一帧;那么栈帧就可以理解成方法进栈的那一刻,就会形成一个栈帧。

每个方法进栈都会有一个对应的栈帧。

此外、栈是先进先出原则,mian方法在最底下,最先进来,最后出去。

这个过程可以用断点调试模拟出来,感兴趣的小伙伴可以去尝试下。

其中方法栈里的线程是不共享的,什么意思呢?

最直接地理解就是:栈里面,不同的线程是独立存在的,所以线程安全。

与之相对的是堆、方法区线程是共享的,这又是什么意思呢?

最直接地理解就是:堆、方法区里面的数据,不同的线程都可以拿过去用,所以线程不安全。

②heep:堆

我们看oracle最新的官方文档对其的说明,最权威的便是这个官方文档,毕竟技术是不断更新的:

6

这是翻译成中文后的页面,原文都是英文,英语好的小伙伴可以直接看,不过翻译大体上也还算是准确的:

  • 堆的线程是共享的(数据共享)
  • 堆的区域是用来存放对象和数组的(new 对象 new 数组,看到new这个关键字就可以想到堆)
  • 堆在Java虚拟机启动时创建。
  • 堆的大小可以是固定的,也可以扩展:使用“-Xms”与“-Xmx”控制堆的最小与最大内存。

此外对象是会创建很多的,其中一小部分会一直被使用到,大部分我们使用后就会舍弃,所以这也是垃圾收集器管理的主要区域。

根据这两种不同的情况,垃圾收集器为了更好地一一应对,堆内存又被划分成新生代和老年代:

  • 老年代:对象会一直被使用到,不会轻易地被回收
  • 新生代:对象朝生夕灭,需要经常回收

其中还有更加详细地细分,实在是有点学不动了,暂时不考虑。

③方法区

同样的,看官方文档:

7

这个翻译有点问题,但大体上也能看:

  • 方法区的线程是共享的。
  • 方法区在Java虚拟机启动时创建。
  • 方法区的作用是存储每个类的结构。

既然是存储类的结构,那么.class文件也就是存储在这个内存区域。

说白了就是类里面的数据,都是存在这个方法区里面的。

类中有什么?

成员变量,成员方法等等,其中方法和变量还分静态和非静态。

这些都是存储在方法区里面的,除了类里的数据,常量池也在方法区里面。

方法运行时,会从方法区进入到栈里面;

创建该类的对象时,会将创建的对象存放在堆里面。

其中静态变量随着类的加载而加载,它并不会进堆。

以上三块便是和我们编写Java代码息息相关的内存区域,除此之外还有两块区域:

④程序计数器

我们平时编写的代码可以被反编译成JVM指令,cpu就是依靠这个程序计数器执行指令的:

8

程序计数器会保存当前执行指令的地址,比如说程序计数器现在保存的为0,那么CPU会执行0对应的指令;

一旦指令执行,程序计数器将更新到下一条指令,上图中也就是3,CPU接着会执行3对应的指令。

就这样依次执行下去,程序计数器的作用就在于保证程序能正常运行。

⑤本地方法栈

①中的栈准确地说应该叫虚拟机栈,它服务的方法是Java方法;

本地方法栈作用和其是差不多的,不同的地方在于它服务的方法不是Java方法而是本地方法;

什么叫本地方法?我们其实见过,如下图:

9

Objcet类中就有本地方法,被native这个关键字修饰,native就是本地的意思,这很好记忆。

也就是说该方法不是由Java语言写的,而是由本地语言写的,比如说C语言。

当然虚拟机规范中并未给出强制规定由什么本地语言写,不同的虚拟机可以自由实现。

以上便是对JVM内存结的说明,除了JVM内存结构外,还有两大块……

三、JVM体系结构

源码通过编译成字节码文件,再被类加载器加载进内存,最后经过各种交互被执行,大致就是这么一个运行流程:

10

①类加载子系统(Class loader SubSystem)

它的作用是将类加载进内存,在Java中对应一个类ClassLoader,该类是一个抽象类。

  • 引导类加载器(Bootstrap ClassLoader)

它是由C++编写的,是用来引导Java程序的,它并不是ClassLoader子类。

  • 扩展类加载器(Extension ClassLoader)

它是由Java程序编写的,负责加载java平台中扩展功能的一些jar包。

  • 应用类加载器:(Application ClassLoader)

它是由Java程序编写的,加载编写的代码。

②运行时数据区(Runtime Data Areas)

也就是JVM内存结构,上述已经详细讲解了,这也是对于程序员来说需要去重点了解的。

③执行引擎(Execution Engine)

解释器interpreter:它是用于读取字节码文件,对其解释并逐一执行,它的解释速度较快,但是执行较慢,并且它有一个致命的缺点,就是当同一个方法被多次调用时,它每次都要解释。

即时编译器JIT Compiler:它是用来优化解释器的,解决上述的缺点。

垃圾回收器GC:收集并回收由new关键字创建的对象。

④本地方法接口和本地方法库

存放本地方法。

总结:

以上便是对Java程序运行的详解,以及对JVM内存的分析。

奈何本人能力实在是有限,还有一些地方法没有弄明白,讲解的也不算很深入,但就我当前具备的能力而言,已经算是最详细的解读了。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-06-06
本文作者:刘小爱
本文来自:“掘金”,了解相关信息可以关注“掘金”

相关文章
|
26天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
51 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
9天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
24 2
|
16天前
|
存储 算法 Java
聊聊jvm的内存结构, 以及各种结构的作用
【10月更文挑战第27天】JVM(Java虚拟机)的内存结构主要包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和运行时常量池。各部分协同工作,为Java程序提供高效稳定的内存管理和运行环境,确保程序的正常执行、数据存储和资源利用。
43 10
|
10天前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
10 2
|
16天前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
21天前
|
Java Maven 数据安全/隐私保护
如何实现Java打包程序的加密代码混淆,避免被反编译?
【10月更文挑战第15天】如何实现Java打包程序的加密代码混淆,避免被反编译?
33 2
|
23天前
|
安全 Java Linux
java程序设置开机自启
java程序设置开机自启
|
25天前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
46 2
|
14天前
|
存储 Java 编译器
[Java]基本数据类型与引用类型赋值的底层分析
本文详细分析了Java中不同类型引用的存储方式,包括int、Integer、int[]、Integer[]等,并探讨了byte与其他类型间的转换及String的相关特性。文章通过多个示例解释了引用和对象的存储位置,以及字符串常量池的使用。此外,还对比了String和StringBuilder的性能差异,帮助读者深入理解Java内存管理机制。
17 0
|
24天前
|
存储 Kubernetes 架构师
阿里面试:JVM 锁内存 是怎么变化的? JVM 锁的膨胀过程 ?
尼恩,一位经验丰富的40岁老架构师,通过其读者交流群分享了一系列关于JVM锁的深度解析,包括偏向锁、轻量级锁、自旋锁和重量级锁的概念、内存结构变化及锁膨胀流程。这些内容不仅帮助群内的小伙伴们顺利通过了多家一线互联网企业的面试,还整理成了《尼恩Java面试宝典》等技术资料,助力更多开发者提升技术水平,实现职业逆袭。尼恩强调,掌握这些核心知识点不仅能提高面试成功率,还能在实际工作中更好地应对高并发场景下的性能优化问题。
下一篇
无影云桌面