JVM内存区域简介

前言

​ 在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堆栈:

  1. 了解栈数据结构;

  2. 了解堆数据结构;

  3. 区分堆内存和堆数据结构;

  4. 了解堆内存和栈内存的联系和区别。

(嗯... 也比较好理解)

5、方法区

​ 虚拟机规范中:不限定实现方法区的内存位置和编译代码的管理策略

​ 虚拟机规范中:方法区保存的也是线程所共有的数据,存储类的结构信息。包括:运行时常量池、字段、方法数据、构造函数和普通方法的字节码内容。

  • 方法区随着jvm的启动而创建
  • 不同的jvm对于方法区有着不同的理解和实现
  • HotSpot (jdk默认) 虚拟机将gc的管理范围扩散到方法区,作为永久代的实现。

方法区这个内存块在很多地方有不同的实现,它的最主要的功能是存放类的结构信息。包括:运行时常量池、字段、方法数据、构造函数和普通方法的字节码。

可以将方法区跟堆内存相连,直接采用堆中的gc来管理方法区内存;也可以单独划出一部分内存作为方法区,并单独写一套内存管理方法;不同的虚拟机允许不同的实现方法,但是功能必须在 虚拟机规范 中。

总结

​ 很多时候,初学者不是很容易的区分堆内存、栈内存、方法区、永久代、新生代等等概念。我们可以为jvm的内存做一个总结;方便我们了解和区分这些概念

  • 从归属区分:

    • 归属线程的:虚拟机栈、本地方法栈、pc计数器

    • 归属jvm的:堆、方法区

  • 从功能区分:

    • 保存对象实例数据:堆
    • 保存类的数据:方法区
    • 保存方法变量:虚拟机栈
    • 保存本地方法变量:本地方法栈
    • 保存线程执行位置:pc计数器
posted @ 2019-05-20 23:20  undifinedException  阅读(156)  评论(0编辑  收藏  举报