前言
在Java运行期,程序会使用到若干内存区,其中一些会随着虚拟机启动而创建,随着虚拟机销毁而销毁。还有些则是与线程一一对应,他们随着线程的开始而创建,线程的结束而销毁。
- Jvm运行时内存区根据生命周期分为2种:1、归属虚拟机的。2、归属线程的。
- 线程描述:开始和结束;内存描述:创建和销毁。
《Java虚拟机规范》(Java SE8 版)第二章第五节规定以下内存区:pc寄存器、Java虚拟机栈、Java堆、方法区、运行时常量池、本地方法栈。
Java内存区域
1、程序计数器
在虚拟机规范中:pc(program counter)寄存器用来保存线程的当前方法。
Java允许多线程执行,每个线程都有自己的计数器,它保存当前线程的执行位置。Java多线程是通过线程轮流切换来并分配处理器执行时间的方式运行(在单核硬件系统中,所有的多线程都是如此运行,例如计算机的cpu时间)。一个处理器每时每刻都只能处理一条线程的任务,所有当多线程切换时,通过pc计数器来记住当前的位置。
- pc计数器属于线程的,每个线程都有自己的计数器。
- 各个线程计数器之间互不影响,独立存储。
- pc计数器用来记住线程执行的当前位置。
2、Java虚拟机栈
虚拟机规范中:Java虚拟机栈(stack)用于保存栈帧。
栈帧:方法运行时的基础数据结构;用于存储局部变量表、操作数栈、动态链接、方法出口信息等。
虚拟机栈只收到栈帧的出入影响,我们很容易知道栈的运行模式时"后入先出"。JVM对于栈的实现中,操作的元素是栈帧。它在内存区中是允许连着堆内存,但它属于线程,操作的元素是"栈帧",所以堆(heap)和栈(stack)的区分在于他们的功能而不是内存位置。
- 栈中操作的数据元素是栈帧。
- 内存可以跟堆连续或者在堆中分配。
- 虚拟机栈是属于线程的。
关于栈ADT。栈其实是后入先出表;在Java中可以用数组或者表来实现。
关于栈帧一。栈帧的数据结构中,最主要的是操作数栈,它是栈的标准实现,是一个后入先出的表。在条件允许的情况下,我们通常称呼"当前栈帧的操作数栈" 为 "操作数栈"
关于栈帧运行原理:每个线程正在处理的栈帧成为"当前栈帧",它的方法称为"当前方法",它的类称为"当前类"。当一个方法创建时,创建它的栈帧,方法结束或者调用新的方法时,新的栈帧随之创建,作为新的"当前栈帧",直到最后方法执行完成,将得到的结果返回给前一个栈帧。使得前一个栈帧重新称为"当前栈帧"。
3、本地方法栈
虚拟机规范中:本地方法栈用来支持native方法。如果虚拟机不允许native方法,或者本身不依赖传统栈,可以不实现本地方法栈。
4、Java堆
虚拟机规范中:Java堆保存线程所共有的数据;堆的实现不要求内存区域上是连续的。
虚拟机规范中:所有对象实例都要在堆上分配。
堆的占比在Java程序中是最大的一部分,随着jvm的启动而创建,保存着线程所共有的信息,例如:对象信息,在大型系统中,对象有可能会有很多!甚至有些程序会在短时间内创建大量的对象。所以堆内存的管理和优化也是很重要的,jvm的垃圾回收算法(C程序开发者最烦内存管理?C程序员最喜欢自己整理内存?)也在不断的升级,就是为了能够更好的管理堆内存,防止出现outOfMemoryError异常。
- 堆内存是属于jvm的
- 堆内存中保存对象实例信息
- 堆内存被gc管理
堆和栈是很多新手小兄弟都会了解但是又一知半解的。推荐下如何正确理解Java堆栈:
了解栈数据结构;
了解堆数据结构;
区分堆内存和堆数据结构;
了解堆内存和栈内存的联系和区别。
(嗯... 也比较好理解)
5、方法区
虚拟机规范中:不限定实现方法区的内存位置和编译代码的管理策略
虚拟机规范中:方法区保存的也是线程所共有的数据,存储类的结构信息。包括:运行时常量池、字段、方法数据、构造函数和普通方法的字节码内容。
- 方法区随着jvm的启动而创建
- 不同的jvm对于方法区有着不同的理解和实现
- HotSpot (jdk默认) 虚拟机将gc的管理范围扩散到方法区,作为永久代的实现。
方法区这个内存块在很多地方有不同的实现,它的最主要的功能是存放类的结构信息。包括:运行时常量池、字段、方法数据、构造函数和普通方法的字节码。
可以将方法区跟堆内存相连,直接采用堆中的gc来管理方法区内存;也可以单独划出一部分内存作为方法区,并单独写一套内存管理方法;不同的虚拟机允许不同的实现方法,但是功能必须在 虚拟机规范 中。
总结
很多时候,初学者不是很容易的区分堆内存、栈内存、方法区、永久代、新生代等等概念。我们可以为jvm的内存做一个总结;方便我们了解和区分这些概念
-
从归属区分:
-
归属线程的:虚拟机栈、本地方法栈、pc计数器
-
归属jvm的:堆、方法区
-
-
从功能区分:
- 保存对象实例数据:堆
- 保存类的数据:方法区
- 保存方法变量:虚拟机栈
- 保存本地方法变量:本地方法栈
- 保存线程执行位置:pc计数器