JVM内存结构常见面试题

JVM内存结构布局

1. 堆(Heap)
2. 虚拟机栈(JVM Stacks)
3. 本地方法栈(Navite Method Stacks)
4. 程序计数器(Program Counter Register)
5. 方法区(Method Heap)

虚拟机栈是什么?

  • 每个线程创建,JVM都会创建一个虚拟机栈,其内部保存一个个栈帧(Stack Frame),对应一个个方法的调用。即一个方法表示一个栈帧。
  • 生命周期和线程一致。
  • 虚拟机栈的默认大小是 1024KB,如果栈空间不足就会抛出StackOverflowError(栈溢出),因为虚拟机栈没有GC。
  • 虚拟机栈的空间由新生代Eden区分配。
  • 主管Java程序的运行,它保存方法的局部变量、部分结果,并参与方法的调用和返回。

设置虚拟机栈大小:参数“-Xss”,例如:-Xss2m,设置虚拟机空间大小2m,一般不建议设置,用默认的即可。

栈帧结构
在这里插入图片描述
局部变量表

保存方法内部的局部变量,以及方法参数,如果不是static方法,局部变量表索引0位置保存的是 this

操作数栈(表达式栈)

方法执行过程中,将数据压入操作数栈中,进行运算(赋值,加减等操作),再将运算结果压入到操作数栈中。满足先进后出原则。

动态链接

每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking)。比如:invokedynamic指令
在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用保存在class文件的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。

方法返回地址

返回值存放在操作数栈中,如果是对象,则存放引用地址指针。

附加信息

可有可无的一些信息

本地方法栈

调用 native 方法

程序计数器

用来存储指向下一条指令的地址,由执行引擎读取该地址执行。
是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖程序计数器完成。
字节码解释器工作时,通过改变这个计数器的值来选取下一条需要执行的字节码指令。

程序计数器的作用

因为CPU需要不停的切换各个线程执行,这时候切换回来当前运行的线程,就得知道接着从哪开始继续执行。JVM的字节码解释器就需要通过改变程序计数器的值来明确下一条应该执行哪一条指令。

方法区

方法区的演进细节
在这里插入图片描述
方法区存放的数据

用于存储已被虚拟机加载的类型信息、运行时常量池、静态变量、即时编译器翻译后的代码缓存、域信息、方法信息。

  • 类型信息(类class、接口interface、枚举enum、注解annotation)
  • 域(Field)信息(域名称、域类型、域修饰符(public、private、protected、static、final、volatile、transient的某个子集)。)
  • 方法(Method)信息(方法名称、返回类型、参数的数量和类型,方法的修饰符,方法的字节码(bytecodes)、操作数栈大小、局部变量表大小,异常表)
  • 运行时常量池(与class常量池一一对应,运行时常量池中有两种类型,分别是symbolic references符号引用和static constants静态常量)
  • 静态变量(存储静态变量的指针引用,而非实例)
  • 代码缓存(即时编译器缓存代码)
  • 字符串常量池(字符串的字面量,例如:String name = “张三”)

需要注意的是:JDK7及之前版本方法区的实现是(永久代),使用的是堆内存。JDK8开始及以后版本方法区的实现是元空间,使用的本地内存


Java8的内存分代改进,为什么要改进?

改进

方法区改用元空间实现,使用的是本地内存。

为什么改进

因为永久代设置空间大小很难确定的,在某些场景下,如果动态加载类过多,容易产生Perm区的OOM。
元空间并不在虚拟机中,而是使用本地内存。因此默认情况下,元空间的大小仅受本地内存限制。
对永久代调优是很困难的。


JVM堆有哪些区?分别作用什么?

1. 新生代(Young Generation Space)

  • 新生对象的实例,存放在新生代中。

2. 老年代(Tenure Generation Space)

  • 对于生命周期长的对象实例,存放在老年代中。

为什么要有新生代和老年代?

假设堆区不分代的情况

那么堆区的内存就会很大,而触发GC需要等堆内存快满的时候触发,如果触发GC那么就需要对整堆进行回收,而堆区中对象有很多,那么STW的时间就会很长。而且每次回收都会对生命周期长的对象进行检查,进行无意义的耗时。

但是我们在开发过程中,大部分都是创建的临时对象,用完即亡的,那么进行分代后,将对象放在新生代中,GC也就只在新生代GC频繁,但能回收大部分垃圾对象。

Java堆分代思想

分代唯一的理由就是优化性能,提升GC回收速度。


新生代为什么要分成Eden,Survivor区?

因为新生代中的GC回收算法是:标记复制,需要将存活对象复制一份移动到一个新的空间区域。


新生代为什么要有两个Survivor区?而且一个Survivor区一直为空?

因为新生代中的GC回收算法是:标记复制,需要将存活对象复制一份移动到一个新的空间区域。既然Eden区的存活对象复制到Survivor区了,那么下次GC的时候,也需要将Survivor区的存活对象复制到一个新的空间区域。


对象什么情况会进入老年代?

  • 参数:-XX:MaxTenuringThreshold=15,默认就是15。GC回收15次后,对象还存活,晋升到老年代。
  • 大对象:Eden区,Survivor区放不下,且进行Minor GC了,直接分配至老年代。
  • 如果Survivor区中相同年龄的所有对象大小总和大于Survivor空间的一半,年龄大于或等于该年龄对象可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

新生代&老年代,Eden&Survivor 分配比例?

新生代 :老年代 ( 1 : 2 )
Eden :S0 : S1 ( 8 : 1 : 1 )

  • -XX:NewRatio=2,这是默认的,表示新生代占1,老年代占2,新生代占整个堆的1/3。
  • -XX:NewRatio=4,表示新生代占1,老年代占4,新生代占整个堆的1/5。
  • -XX:-UseAdaptiveSizePolicy,取消自适应比例分配。默认是启用。
  • -Xmn10M 设置新生代空间大小,优先级高于-XX:NewRatio,但一般不用该参数。

TLAB(Thread Local Allocation Buffer)的作用?

为什么有TLAB

  • 由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的.
  • 为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度。

TLAB是什么?

在这里插入图片描述

  • Thread Local Allocation Buffer:线程本地分配缓存区。
  • 每个线程创建都会分配一块空间区域(TLAB区域),它包含在Eden区域。
  • TLAB本质是三个指针的管理区域:start、top、end,start、end是在Eden区域占位标记作用,top表示已用空间的指针位置。
  • TLAB只是让每个线程有私有的分配指针区间,但实际存放的对象的内存空间,是所有线程可以访问的,只是其它线程无法在这块区域进行对象分配。线程私有分配区
  • 当一个TLAB用满(分配指针top撞上end了),就需要再申请一个新的TLAB区域。原来的TLAB对象不会有所改变。
  • 多线程同时在分配内存的时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,将这种内存分配模式称为快速分配策略。

TLAB缺点?

  • TLAB空间很小,放不下超过TLAB空间的大对象。
  • TLAB空间还剩一点点没有用到,有点浪费。例如:100kb的TLAB,装了80KB,又来了个30KB的对象,就再申请一个新的TLAB,原来的就有20KB浪费了。
  • TLAB允许浪费空间,导致Eden区空间不连续。

TLAB可浪费空间说明

JVM会维护一个最大浪费空间(refill_waste),当TLAB空间不足时
分配对象大于(refill_waste),会在堆区分配。
分配对象小于(refill_waste),就会申请一个新的TLAB区域。旧的TLAB保持不动。

参数“-XX:TLABRefillWasteFraction”调整课浪费空间大小比例,默认是64,1/64TLAB空间大小。

TLAB和refill_waste都会在运行时不断调整的,使系统的运行状态达到最优,参数“-XX:-ResizeTLAB”禁用自动调整。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Me_Liu_Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值