内存分配

java虚拟机在执行程序的过程中会把它所管理的内存划分为5个不同的区域,分别为方法区,堆,虚拟机栈,本地方法栈,程序计数器和堆.其中方法区和堆是线程共用的,其他的则是各个线程私有的.(下图来自于<深入理解java虚拟机>)
这里写图片描述
下面我们来详细了解一下这5个区.

程序计数器

程序计数器是一块很小的内存空间,它的作用是记录当前线程所执行的字节码的行号.为什么需要记录这个行号呢?因为我们知道,java虚拟机的多线程是通过线程的轮转切换和分配处理器的执行时间来实现的.在来回切换的时候,就需要记住上次这个线程运行的行号,才能恢复到上次运行的地方继续运行.

因此,每个线程都会有自己独立的程序计数器.程序计数器记录的是字节码的行号,所以自然也就不存在内存溢出OutOfMemoryError的情况,它也是五个数据区中唯一一个不会产生内存溢出的区.

值得注意的是,当线程正在执行的是Native方法(Native即非java语言写的代码,但是java允许你对它进行调用)的时候,程序计数器为空.

java虚拟机栈

运行时数据区会有两个不同的栈,一个是虚拟机栈,另外一个是本地方法栈,也就是Native方法使用的栈.

与程序计数器一样,java虚拟机栈也是线程私有的.
虚拟机栈中一个很重要的概念就是栈帧,栈的一个元素就是一个栈帧.下面看一下栈帧的概念结构:
这里写图片描述
一个方法调用对应这一个栈帧,方法调用开始就是栈帧入栈,结束就是栈帧出栈,由图可以看到,一个栈帧包括方法的局部变量表,操作数栈,动态链接,返回地址和一些其他信息.

局部变量表需要的空间在编译器就完成了分配,当进入一个方法时,它所需要的变量空间就已经确定了,运行期间不会改变它的大小.
栈帧中的数据可以共享:

int a = 0;
int b = 0;

在这个栈帧中,a 和 b 共享一块内存空间.

在虚拟既规范中,对这个区域规定了两种异常状况:

  • 线程请求的栈深度大于虚拟机允许的最大深度,如果虚拟机不允许动态扩展的话,就会产生StackOverflowError异常;
  • 上面的情况如果虚拟机允许动态扩展,但是扩展时无法申请到足够的内存时,就会抛出OutOfMemryError异常.

java本地方法栈

本地方发栈和虚拟机栈发挥的作用是一致的,只是前者为虚拟机执行java方法服务,后者为虚拟机执行native方法服务.

堆内存是所有线程共用的.

所有的对象和数组都在堆上分配空间,但是现在来说也不是那么绝对了,因为随着JIT(即时编译器)技术的发展和逃逸分析技术的逐渐成熟,栈上分配和标量替换优化技术将会导致一些微妙的变化.

方法区

方法区和堆一样,也是各个线程共享的内存区域.

java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有个名字叫NonHeap,用于区别堆.

方法区存储的内容包括:

  1. 已被虚拟机加载的类信息
  2. 常量
  3. 静态变量
  4. 即时编译器(Just In Time Compiler)编译的代码

虚拟机规范对这个区域限制比较松,虚拟机的实现可以选择不在这里实现垃圾回收机制.但是如果实现回收的话,主要是针对常量池的回收和对类型的卸载(条件比较苛刻).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值