• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

JVM 运行时堆内存分代

武飞扬头像
juejin
帮助61

JVM 运行时内存分配

根据《Java虚拟机规范》,JVM 在执行 Java 程序的时候,会将它所管理的内存划分成若干个数据区域,它们各司其职,有各自的生命周期:

  • 程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器。
  • Java虚拟机栈(Java Virtual MachineStack):每个方法被执行时,JVM 都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。方法被调用直至执行完毕的过程,对应一个栈帧在虚拟机栈中从入栈到出栈的过程。
  • 本地方法栈(Native Method Stacks):与虚拟机栈相似,区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
  • Java堆(Java Heap):今天的主角。它的作用就是存储对象实例,Java 程序运行过程中,几乎所有的对象都在这里。
  • 方法区(Method Area):用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
    • 运行时常量池(Runtime Constant Pool):方法区的一部分,用于存放编译期生成的各种字面量与符号引用。

注:在一些入门学习资料中,经常讲 JVM 所管理的内存笼统地分为堆和栈,它们分别指的是「Java堆」和「Java虚拟机栈」。

在以上的所有数据区域中,Java堆时最大的一块区域,也是被所有的线程共享的一块区域,在虚拟机启动时创建。堆内存里存储着几乎所有的对象实例,因此,它也是程序运行过程中,垃圾回收器进行垃圾回收的主要对象。

堆内存的分代

堆内存之所以要分代,主要是为了方便进行垃圾回收,因为目前大多数主流的垃圾回收器都是基于分代收集理论设计的。

分代收集,是一套符合大多数程序运行实际情况的经验法则,它基于以下两个假说:

  1. 绝大多数对象都是朝生夕灭的。
  2. 熬过越多次垃圾收集过程的对象就越难以消亡。

以上两个假说奠定了堆内存分代划分的基础。在 JDK 1.8 以后,根据被回收对象的年龄,堆内存主要被分为「新生代」和「老年代」两部分。

  • 新生代中保存的对象,大部分都是朝生夕灭的。
  • 老年代中保存的对象,都是难以消亡的对象。

分代带来的好处

通过这样划分,垃圾回收器可以以较高的频率,对新生代中的对象进行回收。在这部分回收过程中,只需要标记出少量继续留存的对象,而不是去标记大量将被回收的对象。对于老年代的对象,则以较低的频率进行回收。这样便兼顾了效率和内存使用率。

新生代又被划分为「Eden」、「From Survivor」和「To Survivor」三部分,这主要与新生代对象回收的算法有关。实际上,垃圾回收器针对堆内存的不同分区,不仅有不同的回收频率,也采用了不同的回收算法。

分代存在的缺陷和解决办法

在带来好处的同时,分代收集也存在很多缺陷。其中最显而易见的一点就是:对象都不是孤立的,即对象之间存在引用关系,当我们把对象划分到新生代和老年代两个区域以后,就会出现对象之间的跨代引用。

解决办法就是在新生代创建一个全局的记忆集(Remembered Set),它将老年代划分成若干小块,并标记处哪些老年代的小块内存存在跨代引用。当对新生代进行垃圾回收时,只要找到被标记的老年代的这一小部分内存进行扫描即可。

虽然在对象引用关系发生变化时维护记忆集带来了额外的开销,但相比起扫描整个老年代仍然是划算的。因为根据前面提到的两条假说,跨代引用相较于同代引用,所占的比例时很小的。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanfbafj
系列文章
更多 icon
同类精品
更多 icon
继续加载