Java JVM内存区域详解
目录
- 程序计数器
- Java虚拟机栈
- 方法/函数如何调用?
- 堆
- 总结
原网页:JavaGuide
JVM在执行Java程序过程中会把它管理的内存划分成若干个不同的数据区域。JDK1.8和之前的版本略有不同,下面会介绍到。
JDK1.8之前:
JDK1.8之后:
这其中线程私有的:
- 虚拟机栈;
- 程序计数器;
- 本地方法栈;
线程共享的:
- 堆;
- 方法区;
- 直接内存(非运行时数据区的一部分)
程序计数器
程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器完成。
Java虚拟机栈
与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是Java方法执行的内存模型,每次方法调用的数据都是通过栈传递的。
Java内存可以粗糙的分为堆内存和栈内存,其中栈就是现在说的虚拟机栈,或者说是虚拟机栈中局部变量表部分。(实际上,Java虚拟机栈是由一个个栈帧组成,每个栈帧中都拥有:局部变量表、操作数帧、动态链接、方法出口信息)。
局部变量表主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不同于对象本身,可能是一个指向对象的起始位置的引用指针,也可能是指向一个代表对象的句柄或其他于此对象相关的位置)。
方法/函数如何调用?
Java栈可以类比数据结构中栈,Java栈中保存的主要内容是栈帧,每一次函数调用都会有一个对应的栈帧被压入Java栈,每一个函数调用结束后,都会有一个栈帧被弹出。
Java方法有两种返回方式:
1、return语句。
2、抛出异常。
不管哪种返回方式都会导致栈帧被弹出。
堆
Java虚拟机所管理内存中最大的一块,Java堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
Java世界中“几乎”几乎所有的对象都在堆中分配,但是随着JTI编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到对上也渐渐变得不那么“绝对”了。从JDK1.7开始已经默认开启逃逸分析,如果某些方法中的对象引用没有被返回或者未被外面使用(也就是未逃逸出去),那么对象可以直接在栈上分配内存。
Java堆是垃圾收集器管理的主要区域,因此也被称作GC堆。从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以Java堆还可以细分为:新生代和老年代;进一步细致一点还有:Eden空间、From Survivor、To Survivor空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。
在JDK7版本以及JDK7版本之前,堆内存通常被分为以下三部分:
1、新生代内存
2、老生代
3、永生代
JDK8版本之后,方法区被移除了,取而代之的是元空间,元空间使用的是直接内存。
大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!