暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java面试一天一题(day 4面试题:谈谈你了解的JVM内存模型?)

架构狂人 2021-06-29
905


    最近工作好累,今天聊点轻松点的,JVM内存模型,相信应该没有人面试这个都不问吧?哈哈,作为一个后端技术人员,程序的运行环境,执行流程必须要了如指掌,否则面对之后的性能调优,线上问题处理,你将束手无策!


网上对JVM在JDK中的组成,相信大家都已经看过了,这里贴出来比较重要的部分:




1 JDK1.6~1.8 JVM内存模型演变

     抛开JDK版本谈JVM内存模型,总之都是片面的,这里从JDK版本的演变聊下JVM的内存模型(下图来自:小傅哥



JDK 1.6:有永久代,静态变量放在永久代;

JDK 1.7:有永久代,但已经把字符串常量池、静态变量,存放在堆上,逐渐的减少永久代的使用;
JDK 1.8:无永久代,运行时常量池、类常量池,都保存在元空间,但字符串常量池仍然存放在堆上;


2 JVM内存结构



方法区(1.8去掉,引入元空间)

(1)也称非堆,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据
(2)被所有线程共享的,会发生OOM

虚拟机栈

(1)每一个方法在执行的同时,都会创建出一个栈帧,用于存放局部变量表、操作数栈、动态链接、方法出口、线程等信息
(2)方法从调用到执行完成,都对应着栈帧从虚拟机中入栈和出栈的过程,随着方法调用,进行创建和销毁



(1)虚拟机中内存最大的一块,几乎所有的对象实例都在这里分配内存
(2)Java堆由年轻代和年老代组成,分别占据1/3和2/3
(3)年轻代分为三个部分,Eden、From Survivor(s0)、To Survivor(s1),占比为8:1:1,可以设置
(4)堆被所有线程共享的,会发生OOM

元空间(直接内存,图中未画出)

从虚拟机Java堆中转移到本地内存,默认情况下,元空间的大小仅受本地内存的限制,即以后不会因为永久代空间不够而抛出OOM异常出现了。jdk1.8以前版本的 class和JAR包数据存储在永久代 PermGen下面 ,PermGen 大小是固定的,而且项目之间无法共用,公有的 class,所以容易出现OOM
 
程序计数器

(1)较小的内存空间、线程私有,记录当前线程所执行的字节码行号。
(2)如果执行 Java 方法,计数器记录虚拟机字节码当前指令的地址,本地方法则为空
(3)不会发生OOM或StackOverflow

本地方法栈

(1)地方法栈与Java虚拟机栈作用类似,线程私有,唯一不同的是本地方法栈执行的是Native方法,而虚拟机栈是为JVM执行Java方法服务的。
(2)本地方法栈会抛出 StackOverflowError 和 OutOfMemoryError异常。
(3)JDK1.8 HotSpot虚拟机直接就把本地方法栈和虚拟机栈合二为一

常量池

从 JDK 1.7开始把常量池从永久代中剥离,直到 JDK1.8 去掉了永久代。而字符串常量池一直放在堆空间,用于存储字符串对象,或是字符串对象的引用

运行时常量

(1)方法区的一部分,存常量(比如static final修饰的,比如String 一个字符串)和符号引用
(2)被所有线程共享的,会发生OOM

3 Java对象的朝夕

3.1 对象组成(HotSpot虚拟机)

对象头:包含两部分

①自身运行时数据:hashcode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳(Mark word)等
②对象类型指针:就是对象指向它的类元数据的指针,虚拟机通过这个指针来确定对象是哪个类的实例

实例数据

用来存储对象真正的有效信息(包括父类继承下来的和自己定义的)

对齐填充

JVM要求对象起始地址必须是8字节的整数倍(8字节对齐),所以不够8字节就由这部分来补充。

3.2 对象相关

    Java采用了动态内存分配方式,可以通过new关键字来构建对象的实例,此时分配的对象存在堆内存中,但是需要根据逃逸分析算法分析处于何种逃逸状态,准确来说,受逃逸分析和TLAB(Thread Local Allocation Buffer)影响,对象可能会被分配到栈上,这里不做讲解。


对象创建,从new指令开始,JVM首先对符号引用进行解析,解析不了说明,字节码文件尚未加载,此时进行类加载过程,符号引用解析完成后,JVM才是真正的在堆内存中分配对象内存,紧接着是零值初始化,这也说明了Java的属性字段无需显示初始化就可以被使用,而方法的局部变量却必须要显示初始化后才可以访问,最后调用对象构造方法。

指针碰撞



     要求堆内存连续规整,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,所分配内存就是把那个指针向空闲空间那边挪动一段与对象大小相等的距离


空闲列表



    虚拟机维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。


3.3 对象的定位


直接指针


栈上的引用直接指向堆中的对象

句柄    

 
    Java堆中会单独划分出一块内存空间作为句柄池,这么一来栈上的引用存储的就是句柄地址,而不是真实对象地址,而句柄中包含了对象的实例数据等信息。好处就是即使对象在堆中的位置发生移动,栈上的引用也无需变化。因为中间有个句柄。

4 小结

    本文从JDK角度分析了JVM的演进过程,内存结构及其作用,包括方法区、虚拟机栈、堆、元空间、程序计数器、本地方法栈、运行时常量等,并学习了对象的组成及其创建过程,相信还是能有所收获的,由于篇幅有限本文没有谈及垃圾回收算法,对象回收方面,不过后面肯定会安排的,敬请期待!


文章转载自架构狂人,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论