前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >堆、栈、方法区—JVM 内存模型分析

堆、栈、方法区—JVM 内存模型分析

作者头像
用户4464623
发布2020-09-10 15:46:07
4440
发布2020-09-10 15:46:07
举报
文章被收录于专栏:晨曦破晓の家晨曦破晓の家

前言

不论是正经或者不正经的程序猿要想变强不能光光变秃,你还必须掌握JVM相关的底层知识。

JVM 内存模型

首先先来看看下面的图这是一张JVM内存模型的一个概况图

348A1B9B-87A2-4af1-BFD0-0EEA8251343C.png

接下来我们对于运行时数据区的五个内存区域做一个简单的介绍:

1. Java虚拟机栈

虚拟机栈描述的是Java方法执行的动态内存模型。当我们的栈空间不足时,就会抛出StackOverFlowError

  • 栈帧:每一个方法执行都会创建一个栈帧,伴随着方法从创建到执行完成。用于存储局部变量表,操作数栈,动态链接,方法出口等信息
  • 局部变量表:存放编译器可知的各种数据类型,引用类型,returnAddress类型。局部变量表的内存空间在编译器完成分配,当进入一个方法的时候,这个方法需要在帧中分配多少内存是固定的,方法运行期间是不会改变局部变量表的大小。

2. 本地方法栈

与虚拟机栈基本类似(栈的空间大小远远小于堆)

  • 虚拟机栈为虚拟机执行Java方法服务
  • 本地方法栈为虚拟机栈执行native方法服务

3. 堆内存

java进程运行过程中创建的对象存放在堆中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。

堆内存是所有线程共有的,下图中的Perm代表的是永久代,但是注意永久代并不属于堆内存中的一部分,同时jdk1.8之后永久代也将被移除。

堆的内存模型大致为如下:

image

默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。 老年代 ( Old ) = 2/3 的堆空间大小。其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。 默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。 JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。 因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间

新生代是 GC 收集垃圾的频繁区域。 当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳

( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。

但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。

4. 程序计数器

几乎不占有内存。用于取下一条执行的指令。

  • 程序计数器是一块较小的内存区域,是当前线程所执行的字节码文件的行号指示器
  • 程序计数器处于线程独占区
  • 如果线程执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果执行的是native方法,这个计数器的值是undefined
  • 此区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域

5. 方法区

方法区也称”永久代“,它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。默认最小值为16MB,最大值为64MB(64位JVM由于指针膨胀,默认是85M),可以通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小。它是一片连续的堆空间,永久代的垃圾收集是和老年代(old generation)捆绑在一起的,因此无论谁满了,都会触发永久代和老年代的垃圾收集。不过,一个明显的问题是,当JVM加载的类信息容量超过了参数-XX:MaxPermSize设定的值时,应用将会报OOM的错误。参数是通过-XX:PermSize和-XX:MaxPermSize来设定的。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • JVM 内存模型
    • 1. Java虚拟机栈
      • 2. 本地方法栈
        • 3. 堆内存
          • 4. 程序计数器
            • 5. 方法区
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档