JVM虚拟机内存划分-运行时数据区域

阅读:59

1.概述

Java程序员和C++程序员不同,C++能够对每个对象的生命周期进行管理。而Java程序员在虚拟机的自动内存管理机制下,不需要关注内存操作。

他们之间各有优缺点,犹如一堵墙,C++程序员虽然可以管理内存,但是稍有不慎很容易出现内存泄漏等问题。Java程序员看似有美好的JVM内存管理机制,不容易出现问题,但是一旦出现内存泄漏,排查起来相当困难,需要对虚拟机相当详细的理解。

2.运行时数据区域

Java在运行过程中,虚拟机会把内存分为若干个区域,根据用途不同,有不同的创建和销毁时间。比如,有的是伴随着虚拟机进程而存在,还有的伴随着线程的启动而结束,线程的结束而销毁。如下图所示:

 

955092-20191020134259798-297378522.png

其中,图中灰色部分是线程共享区域,绿色是线程独有区域。下面对这些区域做详细阐述。

2.1程序计数器

程序计数器,英文是Program Counter Register。存储空间只占JVM很小的一块区域,线程私有空间,作用是线程执行的字节码的行号指示器,或者是当前线程执行到JVM指令地址。字节码解释器就是通过改变这个计数器的值来选去下一条的字节码指令。

线程上下文切换,也是依赖程序计数器完成的。由于每个线程拥有独立的程序计数器,当一个线程恢复运行,需要从程序计数器中获取该线程需要执行的字节码的偏移地址。

如果线程执行的是Java方法,计数器记录的是正在执行的虚拟机字节码指令地址。

如果线程正在执行的是Nativie方法,计数器值则为空(Undefined)。

在JVM规范中,唯一没有发生任何OutOfMemoryError的区域。

 

2.2 Java虚拟机栈

引文是Java Virtual Machine Stack。线程私有空间,生命周期伴随着线程创建而产生,线程销毁而消失。

Java虚拟机栈描述的是Java方法执行的内存模型:

方法执行创建一个栈帧(Stack Frame),存储局部变量表、操作栈、动态链接、方法出口等信息。

方法开始开始时,虚拟机会把这些信息入栈。方法执行结束,虚拟机会把这些信息出栈。

Java虚拟机栈包括如下结构:

  1. 栈帧(Stack Frame)
  2. 局部变量表(Local Variable Table)
  3. 操作数栈(Operand Stack)
  4. 动态连接(Dynamic Linking)

局部变量表,存放的内容为:编译器可知的基本数据类型、引用类型、returnAddress类型(执行了一条字节码指令的地址)。

基本数据类型占用1一个局部变量空间(Slot),long和double除外,占用2个空间。

局部变量表,编译器完成内存分配,运行期间不回概念。

会出现两种异常:StackOverflowErrorOutOfMemoryError

 

2.3 本地方法栈(Native Method Stacks)

本地方法栈和Java虚拟机栈类似,只是Java虚拟机栈是为Java方法服务,本地方法栈是为零Native方法服务。

会出现两种异常:StackOverflowErrorOutOfMemoryError

2.4 堆(Heap)

堆是Java虚拟机管理的内存最大的空间,线程共享,存放对象实例。前期所有的对象和数组都是在对上分配,现在随着JIT编译器的发展与逃逸分析技术的成熟,栈上分配、标量替换优化也可以分配对象。

现在的JVM垃圾收集器,基本上采用分代收集算法,所以堆一版分为:新生代 (Young) 又被划分为三个区域:Eden、From Survivor、To Survivor。

堆内存是逻辑上连续,物理上可以不连续的空间。堆空间可以动态分配,如果扩展无法申请足够内存,会抛出OutOfMemoryError

2.5 方法区

方法区(Method Area),线程共享,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器后的代码等数据。

JDK8版本,已经移除永久代。每个类的运行时常量池、编译后的代码移到了另一块与堆不相连的本地内存 -- 元空间(Metaspace)。

2.6 运行时常量池

Runtime Constant Pool,是方法区的一部分。

运行时常量池(Runtime Constant Pool)内容包括:

  1. 字面量,包括:文本字符串、被声明为final的常量值、基本数据类型的值(基本数据类型的包装类比如Byte、Short、Integer、Long、Character、Boolean也实现了常量池技术,在-128到127之间用常量池)。Float 和 Double没有实现常量池技术。
  2. 符号引用,类和结构的完全限定名、字段名称和描述符、方法名称和描述符号。

当常量池无法申请到内存时会抛出OutOfMemoryError

2.7 直接内存

Direct Memory,不是Java虚拟机规范定义的内存规范,但是会被虚拟机频繁的使用,也会出现OutOfMemoryError异常。

JDK1.4 加入了NIO,基于通道(Channel)、缓冲区(Buffer)的I/O方式。可以直接调用Native函数库直接在Java堆外分配内存,然后通过Java堆中的DirectByteBuffer对象操作直接内存,目的是提高性能,避免Java堆和Native堆中来回复制数据。

所以,直接内存是Java堆之外分配的内存空间,不受堆大小限制,只是受机器的总内存和处理器寻址空间的限制。

总结:JVM虚拟机内存区域分为程序计数器、虚拟机栈、堆、方法区。直接内存不是虚拟机规范的内存空间。

 

 

 

 

 

 

 

 

读后有收获,请打赏。更多精彩内容,请关注微信公众号。有疑问请加QQ交流群:454792501

全部评论

发表评论
更多精彩内容,请关注微信公众号