运行时数据区域(堆 栈 方法区 常量池)和内存分配策略
Posted zhncnblogs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了运行时数据区域(堆 栈 方法区 常量池)和内存分配策略相关的知识,希望对你有一定的参考价值。
内存管理
内存分配和内存释放
内存分配由程序完成,内存释放由GC完成
运行时数据区域
(1)程序计数器(program counter register)
一块较小的内存空间
当前线程所执行的字节码的行号指示器,字节码解释器在工作的时候就是通过改变程序计数器的值来选取下一跳要执行的指令
多线程环境下,线程轮流切换执行,程序计数器保证线程切换之后能恢复到正确的位置
每个线程都有一个独立的程序计数器
线程私有
没有任何异常
java方法,程序计数器的值为当前正在执行的虚拟机字节码指令的地址
native方法,程序计数器的值为空(undefined)
(2)虚拟机栈(stack)
虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的过程中都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息
局部变量表中存放了编译期间可知的各种数据类型:boolean byte char short int float long double 对象引用(long和double占用两个局部变量空间slot)
局部变量表的大小在编译期间确定
线程私有
StackOverflowError:线程请求栈的深度大于虚拟机允许的栈的深度
OutOfMemoryError:Java虚拟机栈动态扩展时无法申请到足够的内存
(3)本地方法栈
虚拟机栈为虚拟机执行Java方法服务
本地方法栈为虚拟机执行Native方法服务
(4)堆
线程共享(java heap)
存放所有的实例对象和数组
Java内存区域最大的一块
垃圾回收的主要区域(GC堆)(garbage collected)(新生代,老年代)(Eden、from survivor、to survivor)
多线程环境,在堆上分配线程私有的分配缓冲区(TLAB)
堆内存可以是物理上不连续的存储空间、逻辑上连续即可
-Xms: JVM堆内存初始分配的大小,通常为操作系统可用内存的1/64
-XMx: JVM堆内存可被分配的最大上限,操作系统可用内存的1/4
-Xms 和 -Xmx 设置为一样大,可以避免在每次垃圾回收之后,JVM重新分配内存
堆 = 新生代 + 老年代 + 持久代
OutOfMemoryError:堆内存扩展时无法获取到足够的内存
(5)方法区
线程共享
存储已被虚拟机加载的类信息、常量、静态变量、即使编译器编译之后的代码
OutOfMemoryError:方法区无法满足内存分配的需求时
(6)运行时常量池
方法区的一部分
class文件中除了有类的版本、字段、方法、接口等的描述外,还有一项是常量池(constant pool table),用于存放编译期间生成的各种字面量和符号引用
在类加载之后存放
除了保存Class文件中描述的符号引用之外,还会把翻译出来的直接引用也存储在常量池中
OutOfMemoryError
内存分配和回收策略
给对象分配内存(对象所占用的内存在类加载完成之后即可确定)
回收对象的内存(可达性分析不可达并没自救成功的对象)
(1)对象优先分配的Eden区(如果启动了本地线程分配缓冲,按线程优先级分配在TLAB)
当Eden区没有足够的空间时,发生MinorGC
-Xms: Java堆初始化大小
-Xmx: Java堆最大内存大小(初始大小和最大大小一样,jvm在垃圾回收之后不会重新分配内存)
-Xmn: Java堆中新生代的大小(新生代=Eden+1个survivor+另一个survivor)
-XX:SurvivorRatio=8 新生代中Survivor和Eden区大小的比值
(2)大对象直接进入老年代
大对象:需要大量连续内存空间的对象(数组、很长的字符串)
(3)长期存活的对象进入老年代
分代收集
对象年龄计数器
对象分配在Eden区,经过一次Minor GC以后,仍然存活,并且大小能被Survivor区容纳,进入Survivor区,年龄设为1,对象在survivor区每熬过一次Minor GC年龄加一,当年龄超过设置的值(默认15),就会晋升到老年代
-XX:MaxTenuringThreshold
(4)动态对象年龄判定
并不是永远等到对象年龄到达MaxTenuringThreshold时,才将对象晋升到老年代
如果在Survivor取,所有同一年龄的对象的总大小大于survivor区大小的一般,该年龄及以上年龄的所有对象被晋升到老年代
(5)空间分配担保
在进行Minor GC之间,会检查老年代的最大可用连续空间是否大于新生代所有对象的总空间
如果成立,说明此次Minor GC 一定是安全的。
如果不成立,检查参数HandlePromotionFailure,是否允许空间分配担保失败
如果允许失败,检查老年代的最大可用空间是否大于历次晋升到老年代的平均大小
如果是,将尝试一次Minor GC,尽管这次Minor GC有风险
如果不是,进行Full GC/Major GC
如果不允许失败,进行Full GC/Major GC
以上是关于运行时数据区域(堆 栈 方法区 常量池)和内存分配策略的主要内容,如果未能解决你的问题,请参考以下文章