深入理解JAVA虚拟机(一)自动内存管理机制
工作N年有余,一直都是使用内存调优,但没有具体了解过细节,最近时间充裕,可以有时间深入理解一下JVM,根据机械工业出版社《深入理解JAVA虚拟机》一书,做一下读书笔记
一、自动内存管理机制
运行时区域分为:
- 方法区
- 堆
- 虚拟机栈
- 本地方法栈
- 程序计数器
1.1程序计数器
线程私有,java虚拟机规范中没有规定任何OutMemoryError情况的区域,如果是一个java方法,计数器记录的是正在执行的虚拟机字节码指令地址,如果是native方法,计数器值为Undefined
1.2Java虚拟机栈
线程私有,虚拟机栈描述的是Java方法执行的内存模型,每个方法执行时会创建一个栈帧(局部变量表、操作数栈、动态连击、方法出口)每个方法调用到执行完成的过程-->栈帧在虚拟机栈:入栈-->出栈
StackOverflowError异常:线程请求深度大于虚拟机允许的深度
OutOfMemoryError异常:如果虚拟机可动态扩展,在扩展时无法申请足够内存
1.3本地方法栈
同虚拟机栈,区别是执行本地方法
1.4Java堆
又称GC堆,Heap虚拟机管理的内存中最大的一块,所有线程共用的,(之前,所有实例对象以及数组都要在堆上分配)
- 新生代
1、Eden
2、From Survivor
3、ToSurvivor
- 老年代
TLAB:java堆中可能划分出多个线程私有的分配缓冲区
-Xmx -Xms
1.5方法区(非堆Non-Heap)(Hotpot虚拟机称之为:永久代Permanent Generation)
所有线程共用的,存储已被虚拟机加载的类信息、敞亮、静态变量、即编译器编译后的代码等数据
-XX:MaxPermSize:设置永久代上线
OutOfMemoryError:方法区无法满足内存分配需求时
1.6运行时常量池(Runtime Constant Pool)
1.7直接内存(Direct Memory)
不是虚拟机中的内存,但可以使用Native函数库直接分配堆外内存,通过Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,避免java堆和Native堆中来回复制
-Xms
OutOfMemoryError
1.8对象的创建
- 指针碰撞(Bump the Pointer)堆内存绝对规则,创建对象分配内存仅仅是把指针向空闲的空间挪动Serial、ParNew
- 空闲列表(Free List)分配的时候从列表中找到一块足够大的空间划分给对象实例CMS基于Mark-Sweep算法的收集器
- TLAB 本地线程分配缓冲区(-XX:+/-UseTLAB参数设定)
即多线程并发修改指针时不安,so把内存分配的动作按照线程划分的不同空间中进行,即每个线程在Java堆中预先分配一 小块内存,称为本地线程缓冲区
1.9对象的内存布局
- 对象头Header
- 实力数据Instance Data
- 对齐填充Padding
1、对象头:
a、运行时数据 Mark Word:哈希码、GC分代年龄、锁状态标志、线程池有的锁、偏向线程ID、偏向时间戳等
b、类型指针:即对象指向它的类元数据的指针,虚拟机通过这个指针确定这个对象是哪个类的实例
1.10对象访问定位
主流的对象访问方式:
- 使用句柄
- 直接指针
两种方式的优势:
- 句柄存储的时地址,对象被移动时只会改变句柄中的实例数据指针,而饮用reference本身不需要修改
- 直接指针方式最大好处是速度快
2OutOfMemoryError异常
2.1Java堆异出
- -Xmx:堆最大值
- -Xms:堆最小值
- -XX:+HeapDumpOnOurOfMemoryError虚拟机出现内存溢出是Dump当前内存堆快照
2.2虚拟机栈和本地方法栈溢出
- -Xoss本地方法栈大小,实际无效
- -Xss:设置栈容量
java虚拟机规范中描述了两种异常
- StackOverflowErro:-Xss减少栈内存容量,可抛出该异常,异常输出是堆栈深度相应缩小
- OutOfMemoryError:
2.3方法区和运行时常量池溢出
限制方法区大小参数,从而间接限制其中常量池内容
- -XX:PermSize
- -XX:MaxPermSize
2.4本机直接内存溢出
- -XX:MaxDirectMemorySize:直接内存容量,如果不指定,默认-Xmx一样。
特点:如果发现OOM之后的Dump文件很小,而程序中有直接或间接使用了NIO,可以考虑是否是直接内存溢出