一、JVM内存结构
前言
本文将从概念上介绍JVM 内存的各个区域、区域的作用、服务对象及可能产生的问题。
提示:以下是本篇文章正文内容,参考《深入理解java虚拟机》及自己的思想总结的一些内容。希望自己越过心里障碍去学习JVM,让JVM学习历程不再枯燥,同时希望各位同行批评指正一起努力、一起进步。
一、运行时数据区域
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些数据区域都有各自的用户、创建时间、销毁时间。根据《Java 虚拟机规范(java SE7 版)》的规定,Java虚拟机所管理的内存结构图如下:
二、各区域作用
2.1程序技术器
程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定时刻,一个处理器(多核处理器是一个内核)都只会执行一条线程中的指令。因此为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序技术器,个条线程之间计数器互不影响,独立存储,这类内存区域被称为“线程私有的内存”。
如果正在执行的是一个java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;
如果正在执行的是Native方法,这个计数器的值就是空。
此内存区域是唯一一个在java 虚拟机中没有规定任何 OutOfMemoryError情况的区域。
2.2.Java虚拟机栈
虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是java 方法执行执行的内存模型。每个方法执行时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用到执行完成的过程,都对应一个栈帧在虚拟机中入栈,出栈的过程。这个栈就是人们经常口中所说的“栈”。java 虚拟机贵方中,对虚拟机栈规定了两种情况:
1. 如果线程请求栈的深度大于虚拟机所允许的深度,抛出StackOverflowError 异常;
2. 虚拟机栈大部分可以动态扩展、如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError。
2.3.本地方法栈
本地方法栈与虚拟机栈作用类似,也是线程私有的,只不过本地方法栈是为了Native 方法服务的。
2.4.堆
Java 堆是java 虚拟机所管理的内存中最大的一块。Java 堆是被线程共享的一块内存区域。在虚拟机启动时创建,用于存放Java 对象实例,也是垃圾收集器管理的主要区域,因此很多时候也被叫“gc 堆”。java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。HotSpot 虚拟机通过-Xmx 和 -Xms 来调节最大值,最小值。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError
2.5.方法区
方法区也是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、编译器编译后的代码等数据。很多人把方法区称为永久代,其实本质上两者不等价,仅仅因为HotSpt 虚拟机的设计团队把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已。该区域上限参数调整为:-XX:MaxPermSize.根据java 虚拟机规范规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError。
运行时常量池: 运行时常量池是方法区的一部分,Class 文件中除了有类的版本、字段、方法、接口等信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后进入方法区的运行时常量池中存放。运行时常量池也受到方法区内存限制,当常量池无法再申请到内存时,将会抛出OutOfMemoryError。
2.5.直接内存
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分、也不是java虚拟机规范中定义的内存区域。 但是这部分内存被频繁的使用。尤其是一些基于内存的大数据计算引擎中,内存都会有堆外内存。这块内存也会导致OutOfMemoryError。 本机直接内存的分配不会受到java 堆大小的限制,但是,既然是内存,肯定还是会受到本机总内存(包括RAM以及SWAP区或者分页文件)大小以及处理器寻址空间的限制。服务器管理员在配置虚拟机参数时会根据实际内存设置-Xmx 等参数信息。但是经常忽略直接内存,使得各个区域总和大于物理内存限制,从而导致动态扩展时出现OutOfMemoryError
总结
本篇文章只是简单入门讲解JVM的结构以及这些区域的作用。下一篇文章将以代码案例的形式展示内存各个区域出现内存异常时的情况,以便后期代码出现异常时能快速定位到哪块区域的异常。