欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java内存区域与内存溢出异常 博客分类: J2EEJava基础 JavaJVM内存溢出线程

程序员文章站 2024-03-24 21:13:04
...

之前根据平时的积累总结了篇博文“Java内存管理”,都是来自于平时的理解和积累,抽周末休息之余,翻阅了《深入理解Java虚拟机》第二章“Java内存区域与内存溢出异常”,将我的理解和总结分享给大家:

JVM的底层是用C++和少量的C完成编写的,所以在开章中作者很有诗意的写到“Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人想出来”。

当Java虚拟机在执行Java程序语言时会把分配给虚拟机的内存划分为以下几个区域:


Java内存区域与内存溢出异常
            
    
    博客分类: J2EEJava基础 JavaJVM内存溢出线程
 
堆(Heap)

方法区(Method Area)

虚拟机栈(VM Stack)

本地方法栈(Native Method Stack)

程序计数器(Program Counter Register)

五部分,其中堆和方法区是供所有线程共享的数据区,另外三部分是独立与线程相互隔离的数据区。

 

程序计数器(Program Counter Register):见名知意,计数器,针对程序的计数器,即当前线程在执行代码(字节码)时的行号指示器,字节码解释器负责调度当前线程的下一条所需要执行的字节码位置,如程序中的判断、循环、跳转、异常以及多线程切换睡眠后醒来需要执行的行号。由此可见为了保证多线程之间的有序调度,针对每个线程都分配了有一个独立的线程计数器,这样就能保证线程之间能相互协调工作,像这样的内存区域也就是针对线程独立运行的数据区,他们之间互不影响,就叫做“线程私有”内存。如果线程调度的是Java方法则计数器中记录的是正在执行的虚拟机字节码地址;如果执行的是Native方法此时计数器则为空。通俗的说程序计数器就是告知JVM中正在运行的线程下一步要执行的字节码。

 

虚拟机栈(VM Stack):VM Stack同Program Counter Register一样也是一块线程私有内存区域,线程私有内存有一个共同点就是他们的生命周期是与当前线程同步的,如果当前线程终结那么对应占用的内存空间也就立即释放。VM Stack内存是在Jvm执行Java方法时由不同栈帧(Stack Frame)所占有的,每运行到一个方法时就会分配一个栈帧,在栈帧内存放该方法的局部变量以及和该方法相关的信息,栈有一个特征就是有进有出,当一个方法被调用的时候那么就对应这个方法的栈帧入栈,方法执行完毕,那么该栈帧也就出栈,释放占用的内存资源。在编译期间可知的基本数据类型(boolean、byte、char、short、int、float、long、double)以及一些指向对象的引用就将存放在虚拟机栈中进行调度,这里所说的对象的引用并不是指的对象的本身,它指的是指向对象的一个地址引用指针,或者是指向代表对象的句柄。所以如果当一个线程如果需要申请的栈内存空间超过虚拟机预先设定的栈空间那么就会抛出*Error异常;还有一种情况是虚拟机栈可以自己根据实际情况动态扩展,如果实际情况需要扩展更大的空间满足线程运行需要,但是此时无法申请到内存空间时就会抛出OutOfMemoryError异常。

 

本地方法栈(Native Method Stack):它跟VM Stack栈类似,同样也是一块线程私有内存,唯一不同的是他服务的对象不是Java方法,而是Native方法,他也会抛出*Error和OutOfMemoryError异常。

 

堆(Heap):这是占用JVM大量内存的一块区域,这块区域不是线程私有内存,而是所有线程共享的,在虚拟机启动时根据事先的虚拟机设定就已经分配了该空间,该区域存放的是大量的实例对象或数组。至于存放该区域的空间对象的生命周期和空间利用都由JVM的垃圾收集器统一管理,如果该区域不能满足新的实例对象所需要的存放空间那么就会抛出OutOfMemoryError异常。

 

方法区(Method Area):与堆一样也是所有线程可以共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态常量、即时编译器编译后的代码 等数据。当他不能满足新的内存需求分配时他会抛出OutOfMemoryError异常。这里还有一个概念就是运行时常量池(Runtime Constant Pool),他是方法区的一部分,是方法区的一个子集,用于存放编译期生成的各种字面量和符号引用,类一旦被加载后这部分内容就被置入到了常量池中。你会发现也正是类信息、常量、静态常量和各种字面量和符号引用组成了class文件,在运行期间可以通过String类得intern()方法将新的数据置入常量池,所以我们可以理解class文件的所有信息都被放入到了方法区(当然包括常量池),常量池也会受到内存的限制,他不能满足新的内存需求分配时他会抛出OutOfMemoryError异常。