认识Java虚拟机及运行时内存 Java技术体系 Java虚拟机 Java虚拟机内存区域 总结

Java技术体系

按功能划分

  • Java程序设计语言

  • 各种硬件平台上的Java虚拟机实现

  • Class文件格式

  • Java类库API

  • 来自商业机构和开源社区的第三方Java类库

  • JDK java Development Kit (Java开发最小运行单位Java程序设计语言、Java虚拟机、Java类库)

  • JRE Java runtime environment (Java运行环境Java类库API中的Java SE API子集和Java虚拟机)

按服务对象划分

  • Java Card:支持Java小程序(Applets)运行在小内存设备(如智能卡)上的平台。

  • Java ME(Micro Edition):支持Java程序运行在移动终端(手机、PDA)上的平台,对Java API有所精简,并加入了移动终端的针对性支持。

  • Java SE(Standard Edition):支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核心API。

  • Java EE(Enterprise Edition):支持使用多层架构的企业应用(如ERP、MIS、CRM应用)的Java平台,除了提供Java SE API外,还对其做了大量有针对性的扩充,并提供了相关的部署支持。

Java虚拟机

HotSpot虚拟机(官方JDK均采用HotSpot VM),BEA JRockit(被Oracle 收购后不再开发)和IBM J9虚拟机;

特定设备,Zing虚拟机是一个从HotSpot某旧版代码分支基础上独立出来重新开发的高性能Java虚拟机;

Apache Harmony ,没有真正地被大规模商业运用过,但是它的许多代码(主要是Java类库部分的代码)被吸纳进IBM的JDK 7实现以及Google Android SDK之中,尤其是对Android的发展起了很大推动作用;

Google AndroidDalvik VM,Android 4.4时代,支持提前编译(Ahead Of Time,AOT)的ART虚拟机迅速崛起,Android 5.0里ART就全面代替了Dalvik虚拟机;

Graal VM被官方称为“Universal VM”和“Polyglot VM”,这是一个在HotSpot虚拟机基础上增强而成的跨语言全栈虚拟机,可以作为“任何语言”的运行平台使用,这里“任何语言”包括了Java、Scala、Groovy、Kotlin等基于Java虚拟机之上的语言,还包括了C、C++、Rust等基于LLVM的语言,同时支持其他像JavaScript、Ruby、Python和R语言等。Graal VM可以无额外开销地混合使用这些编程语言,支持不同语言中混用对方的接口和对象,也能够支持这些语言使用已经编写好的本地库文件;

Substrate VM,是一个构建在Graal Compiler上的,支持ahead-of-time (AOT) compilation的编译及运行框架。

Java虚拟机内存区域

线程私有数据区:虚拟机栈、本地方法栈、程序计数器;

线程共享数据区:方法区,堆。


程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)。此内存区域是唯一在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。

Java虚拟机栈

与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stack)也是线程私有的,它的生命周期与线程相同。

虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。

本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。

HotSpot是将虚拟机栈和本地方法栈合并在一起。

Java堆

对于Java应用程序来说,Java堆(Java Heap)是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。

Java堆是垃圾收集器管理的内存区域,因此一些资料中它也被称作“GC堆”(Garbage Collected Heap,幸好国内没翻译成“垃圾堆”)。

方法区

方法区(Method Area ) 与堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

永久代是虚拟机实现方法区内存管理的一种方式,JDK8 放弃了永久代概念,采用JRockit、J9一样在本地内存中实现的元空间(Meta-space)代替。

运行时常量池

运行时常量池Runtime Constant Pool,方法区中一部分。

Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。

NIO(New input/output)是JDK1.4中新加入的类,引入了一种基于通道(channel)和缓冲区(buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过堆上的DirectByteBuffer对象对这块内存进行引用和操作。

直接内存的大小并不受到java堆大小的限制,甚至不受到JVM进程内存大小的限制。它只受限于本机总内存大小以及处理器寻址空间的限制。

总结

本文主要讲解了关于JVM虚拟机内存的各个数据区域,如下:

接下来会进一步阅读《深入理解Java虚拟机》,并进行更多内容的记录、讲解。

欢迎点赞/评论,你们的赞同和鼓励是我写作的最大动力!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章