前言
与计网的那篇一样,整理我看过的JVM知识。我刷完了B站的视频,配合《深入理解Java虚拟机》使用。最有意思是书中的实战部分,内存结构的时候,触发不同内存区域的OOM!没系统整理时,别问,一问就GG。哈哈,每天尽力更新整理,攒面试人品了!
思维导图
内容太多了,先不展开导图
Java内存区域
导图
内存三大块:
1. 堆
分为年轻代(Eden、From Survivor、To Survivor,大小比例8:1:1)、老年代。
分区原因具体涉及到__GC__
存放内容
对象实例
数组
转化过程:
新对象在eden区分配(内存不够会在老年代分配),一次GC存活则进入s0、s1中,存活15代进入老年代
异常抛出
OOM
2. 方法区(Non-heap)
又称永久代
存放内容:
类信息
常量
静态变量
JIT编译代码
PS:JDK8移除了方法区,把这些数据放到了直接内存的元数据区
常量池信息,存放编译器生成的字面量和符号引用,JDK7移动到堆上存储
异常抛出:
OOM
3. 栈
分为虚拟机栈、本地方法栈。
64位长度long和double占用两个局部变量空间(Slot)其他的占用1个
存放内容
- 局部变量表(编译期完成,对应基本数据类型、对象引用)
- 操作数栈(字节码相关)
- 动态链接
- 方法出口
- 常量引用
- 小对象(无逃逸时,自动回收)
异常
OOM(无法动态扩展时)
SOF(大于允许深度时)
4. 程序计数器
存放当前线程字节码指令地址,无OOM抛出
5. 直接内存
1. NIO
基于通道(channel)和缓存区(buffer)
直接使用Native函数分配堆外内存(__DirectByteBuffer__对象引用这块内存)
- 避免Java堆、Native堆来回复制数据
- 不受Java堆限制(但受本机总内存限制)
垃圾回收
算法分类
-
引用计数法
原理:维护对象的引用计数,为0时回收
问题:- 性能开销
- 循环引用问题
-
标记-清除法
原理:从根节点出发,标记所有可达对象,把不可达对象删除 -
标记-压缩法
原理:与标记-清除法相同,增加了移动到一侧,清空边界以外的点。以消除碎片 -
复制算法
原理:两块相同大小的空间,每次把存活对象放到另一块,清空本块。并把大对象放到老年代。
问题:- 浪费一半空间
- 不适合存活过多的内存区域(老年代)
分代思想
少量对象,适合复制算法
大量存活,适合标记清理、压缩
一些概念
- 什么是可触及性?
以下是可触及的:- 栈中的引用对象
- 方法区的静态成员
- 常量的引用
- JNI方法栈中的引用对象
- 什么时候可复活?
finalize()中复活对象(当然只会复活一次) - 为什么避免使用finalize?
- 无法确定GC的时间
- 为什么不使用更确定性的try catch finally呢?
- 什么是Stop-The-World现象?
多半是由GC引起的出现全局暂停的现象,(其他原因:Dump线程,死锁检查,堆Dump) - Stop-The-World有什么危害?
- Java长时间无响应(老年代满出现这种情况)
- 考虑有集群的时候,会误判宕机,而出现主备切换(扯远了)
术语
name | 别名 | 含义 |
---|---|---|
FUll GC | Major GC | 清理整个堆 |
Old GC | 清理老年代内存 | |
Mxinor GC | Young GC | 清理年轻代内存 |
具体算法
串行收集器(-XX:UseSerialGC)
毫无疑问,串行的效率最高(回收垃圾的效率),但是带来了较长的停顿。使用该参数则表示:
1. 新生代、老年代使用串行回收
2. 新生代使用复制算法
3. 老年代使用标记-压缩
并行收集器(-XX: UseParNewGC)
复制算法的实现,多线程并行复制,当然是在新生代使用。
并行收集器(-XX: UseParallelGC,-XX: UseParallelOldGC)
新生代使用复制算法,老年代使用标记-压缩(默认不并行,使用后面的才并行)
CMS(Concurrent Mark Sweep)并发标记清除算法
这个就要详细说说了。是标记-清除算法的并发实现。
过程如下:
1. 初始标记(串行,暂停用户线程)
由根直接能标记到的对象,速度快
2. 并发标记(与用户线程并行)
标记全部可达对象
3. 重新标记(串行)
暂停整个应用程序,修正标记,准备清理了
4. 并发清理(与用户线程并行)
清理垃圾
5. 并发重置
开始下一个循环
特点:
1. 停顿时间短,多次与用户线程并行
2. 并发时,系统吞吐量降低(GC线程与应用线程一起工作,应用吞土量当然降低了)
3. 清理不彻底(清理时与用户线程并行,期间产生的垃圾只能下次GC回收)
使用注意事项:
1. 老年代使用(对象变化较小,重新标记、清理期间做的工作更加有效)
2. 不能在空间快满的时候清理,会出现Concurrent mod failure错误 。此时会使用串行收集器,得不偿失。
-XX:CMSInitiatingOccupancyFraction 设置触发GC的阈值
几个参数:
-XX: UseConcMarkSweepGC(使用CMS)
-XX: UseCMSCompactAtFullCollection(Full GC后进行碎片整理)
-XX: CMSFullGCBeforeCompaction(进行几次GC后再进行碎片整理)
-XX:ParallelCMSThreads(CMS线程数量)
如何减轻GC压力??
- 软件架构的设计
- 代码编写
- 堆如何分配