java虚拟机中的内存管理、垃圾回收 GC要点
内存区域:
程序计数器:线程当前进行到哪一行指令的计数器
虚拟机栈: 局部变量存放处、即线程的方法内所需的变量等的信息空间
本地方法区:与虚拟机栈类似,但放的是本地Native方法服务
堆:存放对象实例(划分了一片空间) 可分为新生代、老生代 或可分为Eden, From Survivor, To Survivor 用于回收
方法区:记录虚拟机已经加载了的数据 回收困难
运行时常量池:方法区的一部分,记录常量
直接内存:利用I/O直接用到的内存
垃圾回收:
什么需要回收?
引用计数算法:引用+1,失效-1
可达性分析算法: 当一个对象到GC Roots没有任何引用链相连。
引用方式:
- 强引用:类似直接new,或者直接赋值
- 软引用:有用但非必须。 内存不够将会对这些引用列入回收范围进行第二次回收
- 弱引用:不论内存是否够,垃圾收集时都会被回收。
- 虚引用:对生存时间无影响,只在被回收时收到一个系统通知。
怎么回收?
垃圾收集算法:
- 标记-清除 效率不高,清理后内存变碎
- 标记-复制 老年代一般不用 方法:分为Eden From To 一次Eden From清理后复制放到To 一次Eden To 清理后复制放到From 15次后仍未清理进入老年代
- 标记-整理 清除后进行整理,使数据都向一段移动。
- 分代收集 新生代用标记-复制方法,老年代用标记整理方法
发起回收:(在何时进行GC)(方法和时间点选择)
- 枚举根节点:(用处:可达性分析方法的体现。判断是否有与GC Roots完全无连接的对象)
- Stop The World:枚举根节点时要保证停顿所有java执行线程。 原因是某一对象引用关系还在不断变化时则无法判断。
- 安全点:即每个线程到达安全点时才可以暂停。 原因是如果全部线程统一时间,则会因为OopMap产生大量额外空间。 安全点停止有两种方案:一是全部暂停,如不安全则继续跑至安全暂停(一般不用),二是设置一个标志每次经过判断是否需要停。
- 安全区域:即线程在这些区域都可以随时暂停。
垃圾收集器:
新生代
- Serial:单线程收集 简单而高效
- ParNew:多线程版本 并且只有它能与CMS收集器配合工作
- Parallel Scavenge 吞吐量优先,两个参数分别控制最大垃圾收集停顿时间和吞吐量大小
老年代
- Serial Old: 单线程
- Parallel Old:新生代选择Parallel Scavenge老年代只能选这个 注重吞吐量和CPU资源敏感时可用 并行
- CMS
关键的两个收集器
CMS收集器
- 基于“标记-清除算法”
- 初始标记 并发标记 重新标记 并发清除 其中初始和重新两个操作需要stop the world
- 缺点:对CPU非常敏感因为并发;无法处理浮动垃圾因为处理过程是并发的,且发生在stop的标记之后;会产生大量空间碎片 因为是标记清除方法 需要另进行整理操作,此时要停顿长时间
G1收集器
- 能充分利用多CPU 缩短STW的过程 部分收集器停顿进行GC时,G1可并发的使Java程序继续运行
- 内部分代收集
- 区域价值优先 :特点是将java堆划分成大小相等的独立区域,跟踪每个区域的回收价值大小来确定优先回收哪个。
- 初始标记(stop) 并发标记(并发) 最终标记(并行) 筛选回收(并行)
GC:分为Full GC,Major GC, Minor GC
- Minor GC(新生代GC):对象大多朝生夕灭,发生频繁,快
- Major/Full GC(老年代GC):经常伴随一次Minor GC。一般会比Minor慢十倍
GC日志中的Full GC仅代表此次GC过程中出现了stop the world
内存分配和回收策略:给对象分配内存和回收分配给对象的内存
- 对象优先在Eden分配 若Eden无空间先进行一次Minor GC
- 大对象直接进入老年代(尽量避免短命的大对象“长的字符串、数组等”)
- 长期存活的对象进入老年代
- 进入老年代的对象年限动态判断(相同年龄总和大于一半设限、高于则进入老年代)
- 检查老年代可用空间是否大于可能进入的对象平均大小,以此决定是否进行Full GC