【JVM】JVM内存结构

JVM内存模型

在JVM中,内存空间主要分为两大块,每一个大块中,又分为多个小块。

  • 线程共享区
    • 堆内存
    • 方法区(直接内存)
  • 线程私有区
    • 栈内存
    • 本地方法栈
    • 程序计数器

线程共享区

堆内存

何时分配?

java程序启动时自动分配

堆内存中存放了什么内容?

存放对象实例(new的对象),所有的对象实例及数据都在堆内存中分配内存。
在jdk1.7后,常量和静态变量也存放在堆内存中。

如何设置堆内存?

可以通过参数【-Xms】设置起始内存,默认为电脑内存大小 / 64
可以通过参数【-Xmx】设置最大内存,默认为电脑内存大小 / 4

堆内存划分

为了高效的GC回收,jvm将堆内存分为三个区域,其中年轻代和老年代的默认比例为1:2,可以通过参数【-XX:NewRatio】设置

  • 年轻代
  • 老年代
  • 元空间(jdk1.7前为永久代)
年轻代

年轻代的内存分为两个大区,Survivor区又分为两个小区,三个区的默认比例为【8 : 1 : 1】,可通过参数【-XX:SurvivorRatio】设置,具体结构如下

  • eden区(80%)
  • Survivor区
    • From区(10%):
    • To区(10%)

eden区
新创建的对象会分配到eden区中

Survivor-From区(S0区)
存放第一次ygc后,从eden区转移过来的对象

Survivor-To区(S1区)
存放第二次ygc后,从eden区和from转移过来的对象,并将from区和to区进行转换。
每次转换后,会保证To区为空区域,用于保存下一次转移的对象

为什么Survivor区要分为两个区?

  • 为了避免内存碎片的产生
  • 可以保证有一个区域永远为空,用来接收新转移的对象
  • survivor区会保留未晋升且未回收的对象,如果只有一个区,那就会出现多个不连续的内存地址,当eden将大对象转移到survivor区时,会出现没有足够大的内存进行分配,最终直接晋升到老年代中,造成老年代的资源浪费

年轻代的对象如何晋升老年代?

  1. 当survivor区中的对象年龄到达阈值(默认值15)
  2. 当to区被填满时,所有对象晋升至老年代

为什么默认阈值设置为15?

在jvm的对象头信息中,记录年龄的字段占用4个字节,换算为二进制后,最大值为1111,即15

老年代

老年代中存放从年轻代晋升过来的对象,不会轻易进行回收。
为了避免年轻代的三个区之间发生大量的内存拷贝,一些大对象也可以直接进入老年代。

什么是大对象?

  • 数组
  • 长字符串
  • 可以通过参数【-XX:PretenureSizeThreshold】设置大对象的阈值
元空间

元空间通过本地内存实现,可以看作方法区的一种实现,元空间中存放编译后的class文件。
可以通过参数【-XX:MetaspaceSize】设置元空间大小,默认为20.75MB

方法区

方法区在jdk1.8以前称为永久代,存放类型及编译后的代码缓存,在jdk1.7以前,还存放常量和静态变量。

线程私有区

栈内存

栈空间存放局部变量操作数栈动态链接方法出口地址,它是一种快速有效的分配方式,可以通过参数【-Xss】设置栈空间大小
栈没有GC问题,它的生命周期和线程的生命周期绑定。

工作流程

方法执行前入栈,方法执行结束后出栈

本地方法栈

native方法存放在本地方法栈中,由C语言实现

程序计数器

运行速度最快的区域,记录线程指令。
如果现在执行的是java方法,计数器记录jvm字节码的指令地址。
如果是native方法,则记录为undefined

总结

栈是运行单位,解决程序的运行问题
堆是存储单位,解决数据存储问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值