深入理解JAVA虚拟机—第二章java内存区域与内存溢出异常(一)概述以及运行时数据区

这一整套主要用来记录自己学习深入了解JAVA虚拟机这本书的重点知识,方便以后知识梳理

 

第一部分主要记录一下JVM内存中的一些区域

 

程序计数器:

内存中一小块区域,java线程之间切换是通过分配CPU处理时间来决定的,所以需要一个小的存储区域存储当前程序执行的位置,

所以为了在切换线程之间可以准确的回到当前线程执行的位置,需要一个程序计数器,必须得线程私有

如果运行的是java程序,则程序计数器会指向虚拟机指令的地址,如果正在执行的Native方法,则计数器为空,这个区域是java虚拟机规范中唯一一个没有规定任何OutOfMemoryError的区域

Java虚拟机栈:

也是线程私有的,每个方法在执行的时候会创建一个栈帧,

栈帧用来存储局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法在执行的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程

局部变量表存放了编译器可知的各种基本数据类型以及reference类型,它不等同于对象本身,可能是指向对象的首地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象的相关位置,和returnAddress类型(指向一条字节码指令的地址)。

Long和Double类型占用2个局部变量空间,其余的数据类型只占用一个,局部变量表所需要的内存空间在编译器分配完成,当进入方法的时候这个方法在帧中分配多大的局部变量空间是完全正确的,在方法运行期间不会改变局部变量表的大小。

如果线程请求的栈深度大于虚拟机所允许的深度,则跑出StackOverflowError

如果虚拟机可动态扩展(大部分虚拟机都支持动态扩展)。扩展时无法申请到足够的内存资源,则会抛出OutOfMemoryError

本地方法栈:

类似虚拟机栈,区别在于虚拟机栈执行java程序,本地方法栈执行的是JAVA程序中的Native方法,没有强制规定语言和方法以及数据结构。本地方法栈和java虚拟机栈相似也会抛出StackOverFlowError和OutOfMemroyError

Java堆:

对于大多数应用来说Heap是JVM管理的内存中最大的一部分,Heap是所有线程共享的区域,在JVM启动的时候,Heap就是用来存放对象实例的,几乎所有的对象都在这分配,单随着JIT发展,栈上分配、标量替换优化技术将会导致一些变化,所有对象都在Heap上分配并不是那么的绝对了

JavaHeap是垃圾收集器(GC,Garbage Collected)工作的主要区域。

GC,现代的GC主要使用了分代收集算法,所以Heap中还细分了Eden区,FromSurvivor区、ToSurvivor,从内存分配的角度来说,Heap中还可能会为每个线程分配多个线程私有的分配缓冲区(TLAB,Thread Local Allocation Buffer),每个区域都是用来存放对象实例的,进一步划分是为了更好的回收内存,或者更快的回收内存,

根据JVM的规定,Heap可以处于物理上非连续的内存空间中,只需要逻辑连续就可以,当前主流JVM都是通过可扩展来扩展内存的(-Xmx 和-Xms控制),如果在堆中没有完成内存分配,并且堆也无法内存扩展的时候,就会抛出OutOfMemoryError异常

方法区:

Method Area,与javaHeap一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,JVM规范并没有把方法区描述为Heap的一个逻辑部分,但它有一个别名叫做Non-Heap(非堆)。

在最常用的HotSpotJVM中,可能会吧方法区称作永久代,本质上两者并不等价,因为HotSpot团队将GC扩展到了方法区中,使用了永久代来实现方法区

JVM对方法区的限制非常少,除了和Heap一样不需要连续的内存和可以固定大小或者可扩展外,还可以选择不实现GC,相对来说GC在这个区域实现的比较少。

运行时常量池:

Runtime Constant Pool,是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种字面变量和符号引用,这部分文件在Class进入方法区后存放到运行时常量池中。

JVM对于Class文件有严格的格式规范,每一个字节用于存储哪种数据必须符合规范上的要求才会被虚拟机认可、装载和执行,但是对于运行常量池,JVM规范中没有细节要求,不同提供商提供的JVM可以按照自己的需求实现这片内存区域,一般来说,除了保存Class文件中描述的符号引用,还会把翻译出来的直接引用也存储在运行时常量池中。

运行时常量池和Class文件常量池的区别:

运行时常量池有一个特征是动态的,java语言不要求常量一定是编译器才能产生,运行期也可能将新的常量放入池中,运行时常量池在方法区中,所以也会当运行时常量池在添加常量时无法申请到内存也会抛出OutOfMemoryError异常

直接内存:

 Direct Memory 并不是虚拟机运行时数据区的一部分,也不是JVM规范中定义的内存区域,但是这部分内存也被频繁地使用,JDK1.4中加入了NIO(new Input/Output),引入了一个基于通道和缓冲区的I/O方式,使用Native函数库直接分配堆外内存,然后通过一个存储在JAVA堆中的DirectByteBuffer对象作为这块内存的引用进行操作,在一些场景显著提高效率,避免了Native堆和Java堆来回复制数据。

猜你喜欢

转载自blog.csdn.net/qq_16550909/article/details/81301514
今日推荐