JVM (一)内存模型

一。内存结构


1,JVM内存结构

  • 堆内存:是JVM中最大的一块,由新生代和老年代组成。默认情况下新生代按照8:1:1的比例来分配;
  • 方法区:存储类信息、常量、静态变量等数据,是线程共享的区域;
  • 栈:分为虚拟机栈和本地方法栈,主要用于方法的执行;
  • 线程共享:方法区+堆;
  • 线程独享:虚拟机栈+本地方法栈+程序计数器;
注意,Java8之后的内存模型:

2,通过参数来控制各区域的内存大小

  • -Xms设置堆的最小空间大小。
  • -Xmx设置堆的最大空间大小。
  • -XX:NewSize设置新生代最小空间大小。
  • -XX:MaxNewSize设置新生代最大空间大小。
  • -XX:PermSize设置永久代最小空间大小。
  • -XX:MaxPermSize设置永久代最大空间大小。
  • -Xss设置每个线程的堆栈大小。
  • 没有直接设置老年代的参数,老年代=堆空间-新生代

二。Java堆


Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存
Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。可以细分为:
  • 新生代
  • Eden区
  • From Survivor区
  • To Survivor区
  • 老年代
根据Java虚拟机规范,堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。
如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

三。方法区(Method Area)


方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
Java虚拟机规范对这个区域的限制非常宽松:
  • 不需要连续的内存,可以选择固定大小或者可扩展;
  • 可以选择不实现垃圾收集;
垃圾收集在这个区域是比较少的,但并非数据进入了方法区就“永久”存在了。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
很多人愿意把方法区称为“永久代”(Permanent Generation),本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已。
根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
方法执行时的内存状态
方法的执行都是伴随着线程的。
  • 原始类型的本地变量以及引用都存放在线程栈中;
  • 而引用关联的对象比如String,都存在在堆中;

四。程序计数器(Program Counter Register)


是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储。
  • 如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)。
此区域是唯一在Java虚拟机规范中没有规定OutOfMemoryError情况的区域。

五。虚拟机栈(JVM Stacks)


Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。

虚拟机栈描述的是Java方法执行的内存模型:
  • 每个方法被执行时同时创建一个栈帧,存储局部变量表、操作栈、动态链接、方法出口等;
  • 方法调用直至完成的过程,对应着一个栈帧在虚拟机栈中入栈到出栈的过程;
局部变量表
  • 存放了编译期可知的各种基本数据类型、对象引用和returnAddress类型(指向了一条字节码指令的地址);
  • 64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型只占用1个;
  • 局部变量表所需的内存空间在编译期间完成分配,当进入方法时,需要在帧中分配多大的空间是确定的;
JVM栈两种异常状况
  • 线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError;
  • 如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时抛出OutOfMemoryError;

六。本地方法栈(Native Method Stacks)


与虚拟机栈所发挥的作用是非常相似的,区别是本地方法栈是为Native方法服务。
虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,甚至有的虚拟机(譬如Sun HotSpot)直接就把本地方法栈和虚拟机栈合二为一。
与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

七。OutOfMemoryError异常举例


对内存结构清晰的认识同样可以帮助理解不同OutOfMemoryErrors:
  1. Exception in thread “main”: java.lang.OutOfMemoryError: Java heap space
原因:对象不能被分配到堆内存中
  1. Exception in thread “main”: java.lang.OutOfMemoryError: PermGen space
原因:类或者方法不能被加载到永久代。它可能出现在一个程序加载很多类的时候,比如引用了很多第三方的库;
  1. Exception in thread “main”: java.lang.OutOfMemoryError: Requested array size exceeds VM limit
原因:创建的数组大于堆内存的空间
  1. Exception in thread “main”: java.lang.OutOfMemoryError: <reason> <stack trace>(Native method)
原因:同样是本地方法内存分配失败,只不过是JNI或者本地方法或者Java虚拟机发现
  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值