Java运行时数据区

JVM在执行Java程序时会将其管理内存划分为多个区域,图解如下:

其中堆和方法区由多个线程共享,每个线程独占一份本地方法栈、Java虚拟机栈、程序计数器

程序计数器:程序计数器是一小块的内存空间,程序计数器是执行Java字节码的行号指示器,可通过改变程序计数器的值来读取下一条需要需要执行的指令,从而实现程序执行的流程控制。此外程序计数器会记录线程的执行位置,以便线程在切换上下文之后的恢复过程中,继续执行上次未执行完的指令。

Java虚拟栈:在Java中,每一个方法都会对应一个栈帧,在执行某个方法时,就会将栈帧压入虚拟机栈中,当方法执行完成,便弹出栈帧,不涉及GC的垃圾回收。每一个栈帧都会包含操作数栈、局部变量表、动态链接、方法的返回地址。

局部变量表主要存放编译器已知的各种数据类型(boolean、byte、int等)、对象引用

操作数栈是方法调用的中转站,用于存放中间计算结果和计算过程产生的临时变量

动态链接是将常量池中的符号引用替换成内存中的直接引用

本地方法栈:与Java虚拟栈不同的是本地方法栈存放的是Native本地方法的栈帧,Native方法是基于C实现的

堆:堆是运行时数据区中最大的内存区域,主要是用于存放对象实例和数组。大部分对象都会在堆进行分配,为什么这么说呢?因为JDK1.7开始有了逃逸分析的概念,如果对象没有逃逸出当前方法,就可能会在栈内存中分配内存空间,当方法执行结束,随着栈帧弹出而被释放内存。在栈中分配对象内存,在我看来可以减轻堆内存分配压力,更重要的是对象内存会随着该栈帧弹出而被释放掉,不会涉及GC垃圾回收,因此可以减轻GC回收的压力。当然也可以通过参数关闭JVM的逃逸分析。

堆可以细分为:新生代、老年代。图解如下:

 初始分配内存的对象首先会存放在Eden区,当经历一次GC回收之后该对象还未被清除会晋升到survivor区中,每经常一次GC对象未被清理其分代年龄就会+1,对象要进入老年代,需要达到年龄阈值,会将所有对象的年龄进行累积,当超过survivor区的一半时,会取这个值与15进行比较,取较小值作为年龄阈值,超过该阈值升入老年代。

由于堆中存放大量对象,因此最容易出现OOM的情况

备注:在JDK1.8中,class对象以及类变量也会在堆内存中进行分配,字符串常量池从方法区迁移到了堆中

方法区:方法区存放加载后的class类元信息、包括方法信息、字段信息等以及JIT编译后的热点代码。另外方法区中还包含了运行时常量池,运行时常量池中存放编译器产生的字面量以及符号引用。JDK1.6中方法区还包含字符串常量池,但1.8已经迁移至堆中。方法区其实是一种规范,具体实现有两种:永久代和元空间。目前方法区的实现已经改为了元空间,元空间与永久代最大不同在于元空间基于直接内存,永久代会设定具体大小。因此相比于永久代,基于直接内存的元空间出现OOM的概率大大降低,毕竟是基于直接内存,只要本机内存够用,就不存在OOM

字符串常量池:字符串常量池也是池化技术的体现,主要解决String对象的内存消耗问题,实现字符串常量的复用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值