JVM自动内存管理机制
jdk1.6 sun hotspot
运行时数据区域
程序计数器
- 一块较小的内存空间,当前线程所执行的字节码的行号指示器。
- 线程私有的内存
- 可以认为是存储当前线程执行的字节码指令地址,配合cpu调度
- 唯一一个在jvm规范中没有规定任何oom情况的区域
Jvm Stacks
- 线程私有,生命周期同线程
- Java方法执行的内存模型,每一个方法被执行的时候都会创建一个栈帧
- 栈帧:存储局部变量表、操作栈、动态链接、方法出口等信息
- 每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
- 局部变量表
- 这个区域,jvm规范中规定了两种异常:线程请求的栈深度超过虚拟机所允许的深度:stackoverflowError 虚拟机栈如果可以动态扩展,当扩展时无法申请到足够的内存 oom
本地方法栈
sun hotspot 中 本地方法栈和jvm栈合二为一
java heap (堆)
- 一般堆是虚拟机所管理的内存中最大的一块
- Java堆是所有线程共享的一块区域,在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例。
- Java堆可以细分为:新生代和老年代;其中新生代又可细分为:Eden/From Survivor/To Survivor
- TLAB
- 堆的这些划分目的是为了更好的回收内存,更快的分配内存
- 设置堆大小的参数 –Xmx(最大空间)/-Xms(最小空间)/-Xmn(新生代空间)
- 存在异常 oom
方法区
- 线程共享的内存区域
- 主要存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码
- 永久代
- 存在异常oom
运行时常量池
- 方法区的一部分
- 用于存放编译期生成的各种字面量和符号引用
- 存在异常oom
直接内存
对象访问
句柄访问方式
指针访问方式
sun hotspot 采用指针访问方式
实战:oom
垃圾收集器与内存分配策略
对象存活
引用计数算法
宏观定义:jvm 会为对象维护一个引用计数,gc时根据对象的引用计数来判断是否回收该对象,该算法存在对象间互相引用,致使对象的计数大于0的现象,gc无法判 断对象是否可以被回收,这也是Java虚拟机未采用该算法的主要原因
根搜索算法:
Java中GC Roots对象
虚拟机栈中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法中JNI引用的对象
概念:
从上叙GC Roots对象开始向下搜索,当一个对象到GCRoots没有任何引用链相连时,则此对象不可用。
引用的概念
强引用 软引用 弱引用 虚引用
回收方法区
主要回收两部分内容:废弃常量和无用的类
废弃常量:没有任何引用的常量
无用的类:所有实例都被回收;加载该类的ClassLoader已经被回收
该类对应的class对象没有在任何地方被引用
Jvm控制参数:-Xnoclassgc 设置
-verbose:class –XX:+TraceClassLoading
–XX:+TraceClassUnLoading 查看
垃圾收集算法
标记-清除算法
概念:标记所有需要回收的对象,在标记完成后统一回收掉所有标记的对象,后续算法都是在弥补该算法的缺点而衍生出来的不同方式
缺点:
效率问题:标记和清除的效率都不高
空间问题:标记清除后会产生大量的内存碎片
复制算法
概念:将可用内存按照容量划分为大小相等的两块,每次只使用其中的一块。当被使用的这块内存用完了,就将存活的对象复制到另外一块上面,然后把先前的那块内存已 使用的空间一次性清理掉。
目前jvm的做法是:将堆内存新生代和老年代,其中新生代分为 eden和两块survivor空间,比例8:2;新生代采用的就是复制算法
标记整理算法
概念:标记阶段同标记-清除算法,整理阶段:该算法是先让存活对象向一端移动,然后直接清理掉端边界以外的内存
分代收集算法
概念:根据对象的存活周期的不同将内存划分为几块。例如jvm 堆分为新生代和老年代,根据各个代的特点采用以上具体的收集算法。
垃圾收集器gc hotspot jvm 1.6u22
新生代
serial收集器
ParNew收集器
摘要:Serial收集器的多线程版本
-XX:+UseConcMarkSweepGC的默认新生代gc
-XX:+UseParNewGC 强制指定新生代gc 为 ParNew GC
Parallel Scavenge收集器
关注点不同于其他gc,其关注点是:达到一个可控制的吞吐量
-XX:MaxGCPauseMillis 最大垃圾收集停顿时间
-XX:GCTimeRatio 吞吐量大小
-XX:+UseAdaptiveSizePolicy 开启该设置可以不用关注其他一些gc策略细节参数比如:-Xmn,-XX:SurvivorRatio,-XX:PretenureSizeThreshold等
老年代
Serial Old GC
摘要:Serial GC 的老年代版本,单线程GC,采用 标记-整理算法
Parallel Old GC
摘要:该GC使用多线程和“标记-整理”,JDK1.6中才开始提供
CMS GC (Concurrent Mark Sweep)
摘要:一种为获取最短停顿时间为目标的收集器;采用 标记-清除
“浮动垃圾”:CMS清理阶段,用户线程产生的新的垃圾,这些垃圾在本次GC不会被清理掉
默认情况下:CMS在老年代使用了68%的空间后就会被激活,相应参数设置
-XX:+CMSInitiatingOccupancyFraction
缺点:a.CMS 对CPU资源非常敏感 CMS默认启动的回收线程数:(CPU数量+3)/4
b.CMS收集器无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一个Full GC的产生。
c.CMS基于“标记-清除”算法实现,会产生大量的空间碎片
-XX:+UseCMSCompactAtFullCollection开关参数:在fullGC后 进行一次对片整理
-XX:CMSFullGCsBeforeCompaction 设置在执行多少次不压缩的GC后,跟着进行一次压缩。
G1
G1收集器
摘要:基于“标记-整理”算法实现,非常精确地控制停顿,jdk1.6u14开始有测试版本
G1 将整个Java堆划分为多个固定的独立区域,并且跟踪这些区域种的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域
内存分配和回收策略
对象优先在Eden分配
大对象直接进入老年代
长期存活的对象将进入老年代
动态对象年龄判断
如果在survivor空间种相同年龄所有对象大小的总和大于survivor空间的一半,年龄大于或等于该年龄的对象将会直接进入老年代
空间分配担保