1. java内存区域与内存溢出异常
一、运行时数据区域
运行时数据区 | ||
---|---|---|
方法区 | 虚拟机栈(线程私有) | 本地方法栈 |
堆 | 程序计数器(线程私有) |
-
程序计数器(Program Counter Register)
- 线程私有,唯一不会产生OOM的地方。
-
Java虚拟机栈(Java Virtual Machine Stack)
- 它是线程私有的;
- 它主要描述的是:
java方法执行的线程内存模型,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame,它是方法运行期间重要的基础数据结构)用于存储局部变量表、操作栈、动态链接、方法出口等信息。
每个方法被调用直至完毕的过程,就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。 - 两类异常状况:
如果线程请求的栈深度大于虚拟机允许的深度,将会抛出StackOverflow异常。
如果Java虚拟机的栈可以动态扩展,当栈扩展时无法申请到足够的内存,将会抛出OOM异常。
需要注意Hotspot虚拟机的栈容量不可以动态扩展,Classic虚拟机可以。
-
本地方法栈(Native Method Stacks)
- 线程私有;
- 本地方法栈 与 java虚拟机栈的作用相似,区别是,java虚拟机栈为java方法服务,本地方法栈为虚拟机执行本地(Native)方法服务。
- 本地方法栈 与 java虚拟机栈类似,在栈深度溢出或者栈扩展失败时分别抛出StackOverflow和OOM异常。
- Hotspot虚拟机将 虚拟机栈 和 本地方法栈 合二为一。
-
java堆(java heap)
- 线程共享。
- java堆基本上是java虚拟机所管理的内存中的最大一块,它在虚拟机启动时创建。
- 堆的目的是存放对象实例,java中几乎所有的对象实例都在 堆 里面分配内存。
- java堆是垃圾收集器管理的内存区域。
- 将java堆 进行细分是为了更好的进行内存回收、分配,其存储的还都是java实例对象。
- Java堆既可以被实现成固定大小的,也可以是可扩展的。当前主流的java虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms设定)。如果java堆中没有内存完成java实例分配,并且堆也无法扩展时,Java虚拟机将会抛出OOM异常。
-
方法区(Method Area)
- 线程共享
- 用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
- 方法区有一个别名——非堆(Non-heap),目的是与java堆 区分开来。
-
运行时常量池(Runtime Constant Pool)
- 运行时常量池 是 方法区的一部分,受到方法区内存的限制,当常量池无法申请到内存的时候,将会抛出OOM异常。
-
直接内存(Direct Memory)
- 直接内存不是java虚拟机运行时数据区的一部分。但是这部分内存也被频繁使用,也可能导致OOM异常。主要时NIO场景下的堆外内存。
- 配置-Xmx等参数的时候,需要注意直接内存的占用,各个区域的总内存之和不能大于物理机的内存限制;防止OOM的异常。
二、HotSpot虚拟机对象探秘
- 对象的创建
- 对象的内存布局
对象在堆内存的存储布局可以划分为三个部分:- 对象头 (Header)
- 实例数据 (Instance Data)
- 对齐补充 (Padding)
- 对象的定位
2.垃圾收集器与内存分配策略
垃圾收集(Garage Collection,简称GC);需要完成的三件事:
- 那些内存需要回收?
- 什么时候回收?
- 如何回收?
java虚拟机中程序计数器、虚拟机栈、本地方法栈这三个区域随线程而生,随线程而灭。
垃圾回收器主要针对java堆和方法区中的内存分配、回收的情况进行处理。
发表回复