jvm相关-内存结构

Java虚拟机在执行Java程序时将内存划分为堆、虚拟机栈、方法区、程序计数器和本地方法栈等区域。堆是最大内存区域,用于存储对象实例,垃圾收集器负责清理不再使用的对象。年轻代和老年代是堆的两个部分,年轻代通过MinorGC处理,老年代在空间满时触发FULLGC。方法区存储类信息和常量,JDK1.8后使用元空间代替。程序计数器记录执行的字节码行号,本地方法栈服务于Native方法。
摘要由CSDN通过智能技术生成

Java虚拟机在执行Java程序的过程中会把它管理的内存划分为若干个不同的数据区域。每个区域都有各自的作用。分析JVM内存结构,就是分析JVM运行时数据存储区域。

JVM的运行时数据区主要包括:堆、虚拟机栈、方法区、程序计数器、本地方法栈。

其中,堆和方法区是所有线程共享的,栈、本地方法栈和程序计数器则为线程私有的。

1.堆(又称GC堆)
堆是java虚拟机管理内存最大的一块内存区域,堆内存被所有线程共享,主要存放new关键字创建的对象,所有对象实例及数组都要在堆上分配空间。

垃圾收集器就是根据GC算法,收集堆上所占用的内存空间(收集的是对象占用的内存空间而不是对象本身)。

java堆分为年轻代(Yong Generation)和老年代(Old Generation),年轻代又分为伊甸园区(Eden)和幸存区(Survivor),幸存区又分为From Survivor(S0)空间和To Survivor(S1)空间,参考上图。

新创建的对象存储在年轻代中,当年轻内存占满后,会触发Minor GC,清理年轻代内存空间。

老年代存储长期存活的对象和大对象。年轻代中存储的对象,经过多次GC后仍然存活的对象会移动到老年代中进行存储。老年代空间占满后,会触发FULL GC。

注:①Young GC(也叫Minor GC)发生在Eden、S0、S1区;Major GC发生在Old区;而FULL GC是清理整个堆空间,包括年轻代和老年代。

②如果FULL GC之后,堆中仍然无法存储对象,而且堆无法扩展,就会抛出OutOfMemoryError(OOM)错误。

(1)关于年轻代、老年代具体细节

默认的,年轻代和老年代空间大小比例为1:2,可通过参数-XX:NewRatio配置;

默认的,Eden:from:to=8:1:1,可通过参数-XX:SurvivorRatio配置;

Survivor区域中的对象被复制次数最大为15,可通过参数-XX:Max Tenuring Threshold配置。

(2)为什么年轻代要区分Eden区和Survivor区,为什么要设置两个Survivor区

①如果没有Survivor,Eden每进行一次Minor GC,存活的对象就会被送入老年代,老年代很快被填满,接着触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做触发了Full GC)。由于老年代的空间远大于年轻代,每进行一次FULL GC消耗的时间比Minor GC长得多,影响程序执行、响应速度,所以要区分Eden区和Survivor;

②Survivor存在的意义,就是减少被送到老年代的对象,进而减少FULL GC的发生,Survivor的预筛选保证,只有在Survivor经历15次复制还能在年轻代存活的对象才会被送到老年代。

③设置两个Survivor的最大好处就是减少了空间碎片化。如果只有一块Survivor,就会产生下图这种情况(色块代表被对象占用了的空间),假设Eden满了,Survivor中有一部分对象,由于Eden满了所以触发Minor GC,Eden和Survivor中只有一部分存活对象,接着把Eden中存活对象转移到Survivor中,就会导致空间碎片化(碎片空间:Survivor中由于Minor GC死亡的对象占用的空间,也就是下图中“碎片化”指的地方)。

如果有两块Survivor(S0和S1),假设Eden满了,Survivor的S0中有一部分对象,由于Eden满了所以触发Minor GC,Eden中存活的对象以及S0中存活的对象会被复制到S1中(如果S1放不下就放到老年代中),Eden以及S0被清空。这样就保证了Survivor中不会空间碎片化。

2.虚拟机栈
线程私有,是描述java方法执行的内存模型。

用于存储局部变量表、操作数栈、动态链接、方法出口等信息。(存储临时变量、基本数据类型、对象的引用、方法出口等)

3.方法区
存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据等(jdk7及以前称为永久代,jdk8后把它改为元数据空间)。回收目标主要是常量池的回收和类型的卸载,各线程共享。

常量池是方法区的一部分

JDK1.8 使用元空间 MetaSpace 替代方法区,元空间并不在 JVM中,而是使用本地内存。

4.程序计数器
当前线程所执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址(存储程序当前运行的位置),线程私有。

为了线程切换可以恢复到正确执行位置,每个线程都需有独立的一个程序计数器,不同线程之间的程序计数器互不影响,独立存储。

如果线程执行 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是 Native 方法,计数器值为Undefined。

程序计数器这块内存区域是虚拟机规范中唯一没有OutOfMemoryError的区域。

5.本地方法栈
和虚拟栈相似,只不过它服务于Native方法,可能底层调用的c或者c++,线程私有。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值