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
本文作者:刘小爱
本文来自:“掘金”,了解相关信息可以关注“掘金”

相关文章
|
27天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
2月前
|
NoSQL 测试技术
内存程序崩溃
【10月更文挑战第13天】
141 62
|
6天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
19 1
|
21天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
22天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
19天前
|
Java Linux Windows
JVM内存
首先JVM内存限制于实际的最大物理内存,假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制。
18 1
|
1天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
26天前
|
存储 缓存 Java
结构体和类在内存管理方面的差异对程序性能有何影响?
【10月更文挑战第30天】结构体和类在内存管理方面的差异对程序性能有着重要的影响。在实际编程中,需要根据具体的应用场景和性能要求,合理地选择使用结构体或类,以优化程序的性能和内存使用效率。
|
1月前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
173 9
|
2月前
|
存储 算法 Java
聊聊jvm的内存结构, 以及各种结构的作用
【10月更文挑战第27天】JVM(Java虚拟机)的内存结构主要包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和运行时常量池。各部分协同工作,为Java程序提供高效稳定的内存管理和运行环境,确保程序的正常执行、数据存储和资源利用。
50 10