Java内存管理
个人理解 记录一下 不对的地方请不吝指正,谢谢!!!
Java内存管理-运行时数据区
程序计数器:线程私有的较小内存空间,用于记录程序执行的字节码行号指示器。执行Java方法记录正在执行的虚拟机字节码指令地址,执行Native方法,则为undefined。唯一没有OutOfMemoryError的区域。
Java虚拟机栈:描述Java方法执行的内存模型:每个方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。会有StackOverflowError异常和OutOfMemoryError异常。
本地方法栈:虚拟机使用的Native方法执行栈。
Java堆:垃圾收集器管理的主要区域,线程共有,几乎所有的对象实例都要在堆上分配。
方法区:线程共有空间,存储已被虚拟机加载的类信息、常亮、静态变量、即使编译器编译后的代码等数据。
Java垃圾回收算法
1. 标记清除法
首先从跟集合扫描标记能够被回收的对象,然后回收被标记的对象占用的空间。该算法标记和清除两个阶段效率都不高,而且会造成大量不连续的碎片空间,后续有较大的对象无法分配空间时会不得以再次触发垃圾回收。
2. 复制法
复制法是将内存分为两块相同的大小,每次使用其中一块。当某一块使用完,将其中还存活的对象复制到另一块中,然后回收掉第一块。解决了碎片空间问题,需要分配内存时,只需要移动堆顶针,按顺序分配内存即可。但是内存需要分为大小相同的两部分,造成了内存的浪费。
常被用于新生代的回收,但是新生代往往“朝生夕死”,不需要1:1这么大的空间。内存空间被分配为一块Eden区和两块Survivor区,大小比例8:1:1。
新生代收集:
(1)对象会优先存放在Eden和S1区
(2)当Eden和S1区没有空间时,Eden会将存活的内存对象复制到S2区,S1中存活的对象根据对象存活的年代(默认15代,也可以通过-XX:MaxTenuringThreshold配置)决定去哪里,超过15代去老年代,没超过的则复制到S2.
(3)Eden和S1区垃圾回收完成,S1和S2交换角色,永远保证有一块Survivor空间为空。
(4)当Eden和S1区中的存活对象大于S2区,S2则会向老年代申请担保空间。
3. 标记整理法
标记清理法先对需要回收的对象进行标记,将存活的对象向一端移动,然后清理掉端外所有的空间。常用于老年代的回收。
4. 分代收集
针对不同代采用不同的回收算法,新生代中垃圾回收时会发生大量的死亡,只有少量存活,一般采用复制算法。而老年代中存活量较多,往往没有空间进行担保,所以一般采用标记清除或标记整理法
Java垃圾收集器
Serial | Parnew | Parallel scavenge | Serial Old | Parallel Old | Cms | G1(Garbage-first) | |
线程数 |
单线程收集器 |
Serial的多线程版,除了 多线程一起都跟Serial 一样 |
并行多线程 |
Seria的老年代版本, 单线程 |
Paralle Scavenge的 老年代版本,多线程 |
多线程与用户程序并发 执行 |
多线程(并行与并发) |
垃圾对象 |
新生代收集器 (复制算法) |
新生代收集器 (复制算法) |
新生代收集器 (复制算法) |
老年代收集器 (标记整理) |
老年代收集器 (标记整理) |
标记清理(初始标记、 并发标记、重新标记、 并发清除)(标记清除)
|
分代收集(标记整理) |
优点 |
简单高效,单CPU环 境下没有现成交互开销 |
多CPU的情况下,GC时更 有效的利用系统资源 |
能够精确的控制吞吐量, 有GC自适应调节策略 |
可作为 CMS 收集器的后备预案, 在并发收集发生 Concurrent Mode Failure 的时候使用 |
结合parallel新生代,有效 控制吞吐量 |
能与用户线程并发执行, 能够获取最短回收停顿 时间 |
能够利用多cpu、多核并行执行GC,且并发的执行java程序 |
缺点 |
进行圾回收时暂停用 户正常工作 |
进行圾回收时暂停用 户正常工作 |
垃圾收集时用户线程的 停顿时间长 |
进行圾回收时暂停用 户正常工作 |
注重吞吐量,会造成用户线程 停顿时间长 |
对CPU资源敏感、无法 处理浮动垃圾、大量的碎 片空间 |
region 大小和大对象很难 保证一致,会导致空间的 浪费 |
并行:多条垃圾收集线程并行工作,但此时用户线程处于等待状态
并发:用户线程与垃圾收集线程同时执行(不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个cpu上
垃圾收集器用户client端和server端的区别?