在内存管理模块,C语言的程序员堪比皇帝,但是其也肩负着让内存中对象死亡的责任

但是在Java中,不必要担心这个可能性,全部交由了虚拟机管理内存,方便的同时需要我们了解虚拟机,如何去管理内存的

运行时候数据区域:

常见的有:

图片

那么我们分别说明一下上述的区域

1.程序计数器

只是一个较小的内存空间,看做是当前线程所执行的字节码的行号指令器

字节码解释器就是通过这个程序计数器,字节码解释器就是通过改变这个计数器的值来选取下一条需要执行的指令,

因为Java对的多线程就是通过多个线程在对执行权的争抢而做到的改变

这个程序计数器就会在线程切换的时候,保存当前执行的位置,在重新获取线程执行权的时候,继续执行下去

每个程序计数器被每个线程所私有,这个内存区域为线程私有的内存

如果是一个Java方法,则这个计数器记录的是正在执行的虚拟机字节码指令的地址

如果是Native方法,则计数器为空

而且程序计数器将不会发生OutofMemoryError这种情况

2.Java虚拟机栈

Java虚拟机栈是每个线程私有,生命周期等同于线程生命周期,虚拟机栈描述的是Java方法执行的内存模型

每个方法在执行的同时都会创建一个栈帧,用于存储我们的变量表,操作栈帧,动态链接,方法出口等信息.

局部变量表中存放各种编译期可知的基本数据类型,boolean byte int short long

引用类型的起始地址指针,或者一个句柄

由于这些都是在编译期间可以获得具体的长度的,故局部变量表的大小在整个栈空间创建之初,就确定了大小,在运行期间不会发生改变

但是其对应着,会出现异常抛出

请求的栈深度大于虚拟机所允许的深度,那么会抛出StackOverflowError异常

如果在栈进行动态扩容的时候无法申请到足够的内存,会抛出OutOfMemoryError异常

3.本地方法区

类似虚拟机栈,但是其执行的是Native方法

而且Java虚拟机规范中并没有强制如何去实现这个区域,所以可能有多种实现方式,甚至不去实现

但是本地方法区亦会爆出StackOverFlowError和OutOfMemoryError异常

4.Java堆

尝尝有人把JVM的整体分为堆空间和栈空间

在堆空间中,存放着对象的实例,这也是因为Java虚拟机规范中,规定 所有的对象实例都在堆中存储

由于堆空间经常发生GC对象回收,故又称为GC堆,

所以将整个堆空间细分为新生代和老年代

从内存回收的角度来看,这么分配更有利于内存回收

最后说一下整个Java堆得大小,可以通过-Xmx和-Xms来进行控制,如果在堆中没有内存去保存对象的实例,那么会报出OutOfMemoryError的异常

5.方法区

和Java堆一样,是各个线程的共享区域,用于存储被虚拟机加载的类信息,常量,静态变量等在整个类加载所需要的数据

虽然在Java虚拟机规范中将方法区放在了堆中,但是在实际实现中,两者总是被区分开来

他有一个别名 又名永久代,但是在HotSpot虚拟机中,在堆得永久代实现了方法区,将方法区也纳入了GC的管辖范围内

但是这样带了更加容易导致内存溢出的问题,永久代有着 -XX:MaxPermSize的上限

在Jdk1.7中,将永久代中的字符串常量池移出了

在Jdk1.8中,取消了永久代,方法存放在元空间,元空间和堆不相连,但是和堆共享物理内存,逻辑上来说也在堆中

Java虚拟机规范中并没有规定方法区有垃圾回收机制,所以这个区域可以不实现GC机制,因为这个区域存储的数据回收极少会发生,但是在实际生产中,好几个版本虚拟机都因为这个区域经常产生内存溢出的问题

6.运行时常量池

方法区的一部分

用于存储编译器产生的各种字面量和符号引用,

而在Java虚拟机规范上,并没有严格规范如何实现这个区域

运行时的常量池最大的特点就是具有动态性,Java语言不一定要常量必须编译期产生

运行期间也可以产生

比如String的intern()方法

在这个方法区无法申请到足够的内存的时候,也会发生OutOfMemoryError错误

7.直接内存

直接内存不是虚拟机运行时候的数据区的一部分,是服务器内存中的一个区域,但是这部分和Java内存频繁交互.

在Jdk1.4新加入NIO类,就是通过Native函数直接和内存进行交互,完成大量快速的读取

这个区域同样会在扩容的时候,由于各个内存区域大于物理内存限制,产生OutOfMemoryError的异常

发表评论

邮箱地址不会被公开。 必填项已用*标注