JVM 在执行 Java 程序时,会将内存划分为若干个不同的数据区域,不同的区域用途不同,创建和销毁时间也不相同。在 JDK1.8 版本之后对运行时数据区域做了些修改,下面我们分别来看看修改前后的内存区域是怎么样的。
1、JDK1.8之前的JVM内存区域如下图:
2、JDK8之后的JVM内存区域如下图:
3、各区域的的作用:
(1)程序计数器:
当前线程执行的字节码的行号指示器,记录当前线程执行到程序的哪个位置,通过改变计数器的值,可以选取下一条需要执行的字节码指令。该区域是线程私有,是唯一一个不会发生OOM的区域。
(2)Java虚拟机栈:
描述 Java 方法执行的内存模型,每个方法执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用到执行完成,就对应着一个栈帧在虚拟机中入栈到出栈的过程。该区域线程私有,生命周期与线程的生命周期相同。
(3)本地方法栈:
本地方法栈的作用和虚拟机栈的作用非常相似,区别是本地方法栈则为Native方法服务,而虚拟机栈为执行java方法服务,该区域也是线程私有。
(4)Java堆:
用于存储对象实例,是占用内存最大的区域,可划分为新生代和老年代,新生代又可细分为 Eden区、From Survivor区、To Survivor区。
在 HotSpot 中,对象在堆内存布局分成三部分:对象头,实例数据,对齐填充。
① 对象头:包括两部分的信息:
- 运行时数据:用于存储对象自身的运行时数据,如哈希码,GC代年龄,锁状态标志、线程持有的锁、偏向线程ID等。
- 类型指针:即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是一个Java数组,那对象头中还必须有一块用于记录数组长度的数据。
② 实例数据:是对象真正存储的有效信息,是在程序代码中所定义的各种类型的字段内容,相同宽度的字段会被分配到一起。
③ 对齐填充:并不是必然存在的,仅起着占位符的作用。
(5)方法区:
用于存储类信息,包括运行时常量池、静态变量、常量、即使编译后的代码(即class文件)等数据。与Java堆一样,该区域被所有线程共享。
JDK8 之前,Hotspot 中方法区的实现是永久代(Perm),JDK8 开始使用元空间(Metaspace),以前永久代所有内容的字符串常量移至堆内存,其他内容移至元空间,元空间直接在本地内存分配。
为什么要使用元空间取代永久代的实现?
(1)字符串存在永久代中,容易出现性能问题和内存溢出。
(2)类及方法的信息等比较难确定其大小,所以指定永久代的大小比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
(3)永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
参考文章:https://www.cnblogs.com/czwbig/p/11127124.html
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/114740.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...