java内存区域的理解

java内存区域

  1. 程序计数器
  2. 方法区
  3. 本地方法栈
  4. 虚拟机栈

程序计数器

程序计数器是一块很小的内存区域,java各个线程都有自己的程序计数器用来记录下一条指令的地址。

方法区

在jdk1.8之前方法区的实现方式是永久代,在jdk1.8之后,实现方式换成了元空间。永久代与元空间的区别在于,永久代使用的是jvm内存,而元空间使用的是直接内存,这样的话就能避免因为jvm内存不足导致的outofMemoryError异常的发生。而且在jdk1.6之前,字符串常量池是存在于方法区当中的,在jdk1.6之后常量池被移到了堆中。关于字符串常量池:如果我们temp = new String(“aa”),那么会在将字符串aa放在堆和常量池中,并且temp的引用指向堆中字符串“aa”的地址。关于字符串,有个重要的函数intern函数,在1.6之前intern所做的操作是:判断字符串“aa”是否在常量池中,如果在常量池中直接返回池中的引用,如果不在那么将字符串对象添加到常量池当中,并且返回常量池中的引用。在1.6之后intern函数还会将堆内存中"aa"字符串的引用放到常量池当中。

本地方法区

虚拟机栈

虚拟机栈也是一块非常重要的区域,他会为每个方法在栈空间中新建一个栈帧,栈帧主要包括四大部分

  1. 局部变量表
  2. 操作数栈
  3. 动态链接
  4. 返回地址

其中局部变量表和操作数栈的关系十分密切,我们知道java中执行命令是由程序计数器取字节码指令之后,根据字节码指令执行的,那么当我们进行相应的运算的时候,拿 a + b运算举例,程序计数器会首先将一个常量值压入操作数栈当中,然后再给a幅值为该常量值存放到局部变量表当中,将常量值压入到操作数栈当中,然后再将该常数值赋给b保存在局部变量表中,执行加法指令的时候会将局部变量表当中a的值和局部变量表中b的值读入到操作数栈当中,进行相应的运算之后再存入到局部变量表当中。
在该方法完成所有操作之后,根据返回地址恢复现场,回到方法调用之前的下一个位置继续执行,并且将操作数栈的指针和局部变量表的指针重新指向调用该方法的主方法对应的操作数栈和局部变量表,释放相应的栈帧空间。
其中动态链接是指向堆区域中该对象的内存地址。

首先堆当中保存的是new出来的对象实例。堆中的内存区域分为新生代和老年代。
刚开始的时候新生代的内存区域没有进行划分,就是一整块儿的区域,采用标记删除算法进行垃圾回收,但是呢,标记删除算法有一个很大的缺点,他会产生很多碎片化的空间,虽然堆中总的内存还足够用,但是可能无法分配到一块连续的内存区域供对象使用,不得不提前gc,而且空间碎片化会导致gc更加频繁。为了解决这个问题,后来有人提出了标记复制算法,将新生代内存分为活动区和空闲区两大部分。每次gc的时候将活动区中的对象复制到空闲区中,并且一次性清理活动区的内容,这样不管分配空间还是清理空间都会变得相对高效,但是复制算法也有他的问题,就是内存的浪费太大了,要浪费一半的空间,这简直是不能接受的。后来IBM做了一个统计,发现新生代中能熬得过一次gc的对象很少,根据这个情况,将新生代分为了三大部分eden区和两个survior区,复制算法是将eden区和第一个survior区当中的存活的对象放到第二个survior区,这样就有90%的区域用到了,而且效率也有所提升。
但是如果survior区不够用怎么办,那么老年代就出场了,这里有个分配担保机制,survivor中装不下的话直接进入老年代,如果老年代也装不下那么就进行full gc,如果full gc还是装不下,直接outofMemory异常抛出去。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值