JVM学习——内存分配

       在说明JVM内存分配之前,先说明java程序运行的过程,首先.java文件被java编译器编译成.class字节码文件,然后通过JVM中的ClassLoader类加载器,将字节码文件加载进内存 ,加载完毕后,由JVM的执行引擎执行,在执行过程中,JVM会使用一些空间来保存执行过程所用到的数据和信息,这段空间一般被称为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存。在Java中的内存管理一般指的就是对这个区域的管理。

    一般JVM内存包含以下几个部分:

  

    1.程序计数器:它与物理的CPU寄存器所拥有的功能是相同的,都是用来保存下一个指令所在的存储单元的地址,可以看作当前线程指向下一个可执行字节码的行号指示器。在JVM中每个线程都是轮流获取CPU的执行权,也就是说在同一时间,只能有一个线程来执行指令,为了保证线程在CPU切换过程中能够恢复程序之前执行的位置,因此每个线程都有一个程序计数器,并且是每个线程私有的。当线程执行非native方法时,程序计数器保存的时下一条指令的存储单元的地址,当执行native本地方法时,程序计数器的值为undefined。

    2.java栈:java栈是由一个个栈帧组成的,每个栈帧对应一个方法,包括局部变量表、操作数栈、常量池引用、方法返回地址,当一个线程调用一个方法时,就会创建一个栈帧,并将栈帧压栈,当方法执行完毕,就会将栈帧出栈,由此可以看出,当前运行的方法所在栈帧一定是位于栈顶。

       经常有人把Java内存区分为堆内存(Heap)和栈内存(Stack),这种分法比较粗 糙,Java内存区域的划分实际上远比这复杂。这种划分方式的流行只能说明大多数程序员最 关注的、与对象内存分配关系最密切的内存区域是这两块。“栈”就是现在讲的虚拟机栈,或者说是虚拟机栈中局部变量表部分。

       局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、 float、long、double)、对象引用(reference类型,它不等同于对象本身,可能是一个指向对 象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和 returnAddress类型(指向了一条字节码指令的地址)。 其中64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据 类型只占用1个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这 个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变 量表的大小。

       在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚 拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部 分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。  

    3.本地方法栈:它与java栈非常类似,只不过java栈是为java方法提供服务的,而本地方法栈是为native本地方法提供服务的。

      在虚拟机规范中对本地方法栈中方法使用的语言、使用方式 与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚 拟机(譬如 Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法 栈区域也会抛出StackOverflowError和OutOfMemoryError异常。 

    4.堆:java堆是用来存储对象以及数组,并且是被所有线程共享的。

    5.方法区:在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量(如文本字符串)和符号引用(如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符等)。

       在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。

       在JVM规范中,没有强制要求方法区必须实现垃圾回收。很多人习惯将方法区称为“永久代”,是因为HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。不过自从JDK7之后,Hotspot虚拟机便将运行时常量池从永久代移除了。

      6.直接内存。直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规 范中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError 异常出现。 在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓 冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储 在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。 显然,本机直接内存的分配不会受到Java堆大小的限制,但是,既然是内存,肯定还是会受到本机总内存(包括RAM以及SWAP区或者分页文件)大小以及处理器寻址空间的限制。服务器管理员在配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制), 从而导致动态扩展时出现OutOfMemoryError异常。

       本文部分片段来自《深入理解JAVA虚拟机》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程简介: 目前,Java是最为流行的编程语言之一,它的基础平台就是JVM。除了Java,如JRuby、Scala、Clojure等语言也运行在JVM平台。熟悉和掌握JVM平台有着重要的实用价值和意义。 在本课程中个,将详细介绍JVM的基本原理、组成以及工作方式,并配合实际案例,介绍相关的调优技巧。 课程大纲: 第一课 初识JVM JVM分类 Java语言规范 JVM规范 介绍JVM的基本知识和发展历史,并介绍了Java语言规范和JVM规范。 第二课 JVM运行机制简介 堆、栈、方法区等 JVM启动流程 内存模型和volatile实例 解释和编译运行的概念 介绍JVM的内部结构、启动流程以及内存模型。并介绍JVM字节码的执行方式。 第三课 常用JVM参数 堆的分配参数 栈分配及实例讲解 server与client模式 调试跟踪参数 介绍常用的JVM参数,包括内存分配、堆栈分配、虚拟机运行模式以及调试跟踪参数。 第四课 GC的算法和种类 引用计数 标记清除 复制算法 标记压缩 可触及性 本章是理论性较强的一章,主要介绍GC的基本算法和思想,本章作为下一章节的前序课程。 第五课 GC控制参数 Serial ParNew等GC参数 GC的参数搭配实例分析 介绍GC的设置参数,并分析相关的案列。 第六课 类装载 class装载流程 ClassLoader模式 ClassLoader的使用实例分析 热替换例子 详细介绍ClassLoader的原理和应用。分析2个案例,说明ClassLoader的使用。 第七课 性能监控工具 线程死锁分析 OOM分析 介绍常用的JVM诊断和分析工具,并以死锁和OOM为例,展示这些工具的使用。 第八课 分析Java堆 MAT的使用案例 Jvisualvm介绍使用 介绍了Java堆的分析方法,以一个实例为基础,展示对堆的分析过程。 第九课 锁 baise锁 轻量级锁 自旋锁 介绍JVM中对多线程锁的实现。 第十课 class文件结构 ASM库介绍 介绍JVM规范中的最重要的内容——Class文件结构,同时介绍ASM库的使用以及对class文件的修改。 第十一课 字节码执行 案例以及javap JIT及相关参数 介绍JVM的字节码以及反汇编方法,同时介绍JIT相关的参数和应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值