1:GC
回收区域:
堆和方法区(线程共享的区域存在垃圾回收,线程私有的区域不存在垃圾回收)
栈是线程销毁才会回收,栈帧是某次方法调用后返回才销毁(自动回收,无需GC)
Java语言不用程序员自己分配内存,也不需要自己回收内存,原因就是JVM中实现的垃圾回收机制(自动回收)
方法区:
保存静态变量和类信息,很少回收,比如老版本的JVM就不会回收方法区,所有会产生内存泄漏
堆:
回收的主要区域
发生GC的时间:
对象进入老年代或者新生代,如果该区域空间不足则会触发该区域的GC。
新生代GC:
minorGC,采取复制算法,效率比较高
老年代GC:
majorGC,标记清除算法,标记整理算法,效率比较差
1:死亡对象的判断算法
1:引用计数算法
给对象增加一个引用计数器,当有一个地方引用它,则+1,当引用失效-1,任何时刻当计数器为0则不能在被引用,即对象已死,变成垃圾。
JVM没有采取
这种方式,无法计算循环引用问题
2:可达性分析算法
GC Roots:
垃圾回收要检查的根节点
引用链:
某个对象到GC Roots的路径,对象间的引用关系表现出的连接关系
不可达对象:
一个对象到GC Roots是不可达的,就是一个垃圾
2:垃圾回收算法
1:老年代回收算法-----标记清除算法
流程:
先将需要回收的对象一个一个进行标记,再对标记的对象一个一个进行删除
效率问题:
效率不高,标记和回收时需要进行遍历
空间问题:
回收后产生大量不连续的内存空间,内存碎片
2:新生代回收算法-----复制算法
流程:
将内存区域划分为两个大小相等的空间,在回收时,将存活的对象复制到另外一块未使用的内存空间中,清除之前使用的空间
使用场景:
大多数对象具有朝生夕死的特性(很多方法调用,使用局部变量等于new的对象,方法执行结束这些对象即可回收),新生代对象符合朝生夕死的对象
空间问题:
空间利用率只有50%
新生代回收算法的改进:
JVM中新生代的回收算法是基于复制算法的优化版本,因为新生代的对象很多都是朝生夕死的,所以并不需要按照1:1划分内存,而是将新生代分为较大的Eden(伊甸园)和两块较小的Survivor(幸存者)空间,ES比为8:1,空间利用率达到90%。每次只使用伊甸区和其中一块幸存区,称为from区,另一个幸存区称为to区。回收时,将from区存活的对象复制到to区,再清理from区。如果一个对象再两个存活区交换15次,则会放入到老年代。
3:老年代回收算法-----标记-整理算法
流程:
标记存活对象,将存活对象往一侧移动,然后清除端边界另外一半的空间
4:分代算法
流程:
JVM采取的算法,本质上没有具体算法实现,只是通过区域划分,实现不同区域的不同垃圾回收策略
将内存(堆)
划分为新生代,一个E区,两个S区,以及老年代。
针对新生代采取改进的复制算法,老年代使用标记清除或者标记整理算法。
新生代和新生代:
一般创建的对象放入新生代,但是大对象(对象占据的空间超出JVM设置的阈值)创建后进入老年代。新生代中连续交互15次的对象。新生代GC时,分配担保失败的对象(8+1中存活的对象放到另外一个1中,如果放不下则放入老年代)
3:垃圾回收器
新生代收集器:
- Serial
- ParNew
- Parallel Scavenge
老年代收集器:
- Serial Old
- Parallel Old
- CMS
全堆收集器:
- G1
垃圾回收线程:
守护线程,可并行执行,用户线程可以和垃圾回收线程并发执行或者等待(STW,Stop the world)
吞吐量:
执行用户代码时间/(执行用户代码时间+垃圾回收时间),表达出一个程序的效率好不好
1:CMS垃圾收集器—老年代
Concurrent Mark Swap:
并发标记清除
1:老年代收集器
2:标记清除算法
3:用户体验优先,垃圾回收线程和用户现场同时执行,存在局部的STW(少许时间暂停用户线程)
4:并发收集,低停顿
CMS搭配新生代的ParNew收集器,以此给用户更好的体验
收集过程:
- 初始标记:标记GC Roots能直接关联的对象(这个阶段需要STW)
- 并发标记:进行GCRoots引用链追踪的过程,搜索引用路径(这个阶段可以和用户线程并发)
- 重新标记:修正并发标记阶段,和用户线程并发执行时,修复标记产生变动的记录(这个阶段需要STW)
- 并发清除:并发清除垃圾,和用户线程并发
缺点:
- 对CPU资源比较敏感(可能产生CPU不足的问题)
- 无法处理浮动垃圾(是第四阶段时用户线程并发执行时产生的垃圾)
- 需要预留空间给浮动垃圾
- 不知道具体预留空间大小,如果浮动垃圾超出预留空间大小则产生失败 Concurrent Model Failure (并发模式失败)
- 超出预留空间则会再次触发一次老年代的GC,且是Serial Old单线程的收集器回收
- 标记清除算法本身的空间碎片问题的产生,且可能导致提前触发GC
2:G1垃圾收集器—全堆的垃圾回收器
使用G1收集器时,堆的内存划分:
划分为一个一个的region区,动态分配为E区(伊甸),S区(幸存),T区(老年)
特性:
- 全堆收集器
- 整体看基于标记-整理算法,局部看是基于复制算法
- 用户体验优先的收集器
新生代回收步骤:
回收多个E区和S区,复制存活对象到空的region,再重新定义为S区
老年代回收步骤:
-
初始标记阶段:标记GC Roots能直接关联的对象(这个阶段需要STW),可以和新生代GC同时执行
-
并发标记:进行GCRoots引用链追踪的过程,搜索引用路径(这个阶段可以和用户线程并发),优先回收(Garbage First)存活率低或者几乎没有存活的region
-
最终标记阶段:修正并发标记阶段,和用户线程并发执行时,修复标记产生变动的记录(这个阶段需要STW)
-
筛选回收阶段:筛选存活率低的region,可以和新生代GC同时执行
2:JMM
JMM:
Java内存模型,用来屏蔽各种硬件和操作系统的内存访问差异,实现让Java程序在各平台下能达到一致的内存访问效果
中间内存:
属于硬件层面的中间内存,而不是JMM的概念
主存:
存放线程共享的数据
工作内存:
存放线程私有的,以及共享变量的拷贝
8大原子性的字节码指令:
用于主存和工作内存的数据读取操作
Java内存模型的三大特性:
- 原子性:八大字节码层面的原子性指令,基本数据类型的访问具备原子性
- 可见性:当一个线程修改了共享变量的值,其它线程能够可见
- 有序性:本线程内观察,所有的操作都是有序的,在另一个线程观察所有操作是无序的