目录
a. jvm虚拟机
从软件层面屏蔽不同操作系统在底层硬件与指令集上的区别
b. 栈
栈.栈帧:局部变量表 + 操作数栈 + 动态链接 + 方法出口
栈.动态链接: 符号引用转变为直接引用,通过方法名符号在内存区直接引用的位置(可以理解为代码或者指令码)
栈里面存储的局部变量是对象的话(存储的是该对象的 指针地址),该对象的实际数据是保存在 堆内存中
c.堆
存放new 出来的对象信息
d. 概念
方法区(元空间): 常量 + 静态变量 + 类信息(存放代码的位置)
静态变量如果是对象的话(方法区中的静态变量存储的是指针),该对象的实际数据是保存在 堆内存中
本地方法栈: 保存本地方法的地方,被 native 修改的方法,该类型的方法的实现是使用 c 或其他语言 实现
程序计数器:处理器切换线程时并不会记录上一个线程执行到哪个位置,所以为了线程切换后依然能恢复到原位,每条线程都需要有各自独立的程序计数器
e.堆内存空间的划分
年轻代:1/3
年轻代.Eden: 1/3 中的 8/10
年轻代.servivor0: 1/3 中的 1/10
年轻代.servivor1: 1/3 中的 1/10
老年代: 2/3
新 new 出来的对象,被保存在 eden 区,
字节码执行引擎,开启线程对 Eden 区进行 minor gc 垃圾回收
字节码执行引擎,开启线程对 老年代 区进行 full gc 垃圾回收
f. gc 过程:
1. 从 gc root 根节点触发,找到所有引用的引用对象,标记为非垃圾对象,将这些 “非垃圾对象” 复制到 servivor0 区域,这个时候 Eden 区保存的对象信息就属于是垃圾对象,通过 minor gc 线程进行清理回收。
2. 程序一直在运行,就会一直产生对象放到 Eden 区,当 Eden 区再次被放满之后,再次进行 minor gc ,将 Eden 区的 “非垃圾对象” + servivor0 中存活的对象,全部复制到 servivor1 区域, 这个时候 Eden 区 + servivor0 区 中剩余的对象全是垃圾对象,通过minor gc 线程进行垃圾回收。这个时候 Eden 区 和 servivor0 是没有数据的,servivor0 为下一次的 minor gc 做准备,接收来自 servivor1 + Eden 中的非垃圾对象。
3. 当 Eden 区,再次被放满之后,会再次进行minor gc 的操作,将 Eden 区的 “非垃圾对象” + servivor1 中存活的对象,全部复制到 servivor0 区域, 这个时候 Eden 区 + servivor1 区 中剩余的对象全是垃圾对象,通过minor gc 线程进行垃圾回收。
4. 非垃圾对象,每进行一次 复制操作,该对象的 分代年龄+1,当分代年龄 到达15 的时候,会被转移到 老年代(分代年龄,保存在对象头里面(现在对象头是 64 位))
5. 当老年代的内存空间被存满之后,会进行 full gc
6. 当老年代中的 对象大部分都是 非垃圾对象, full gc 结束之后,仍然无法保存,新生代传递过来的数据时,会发生内存溢出 OOM
g. jvm 调优
主要减少 full gc 次数,minor gc 太频繁也要进行调整
Stop-The-World机制简称STW,是在执行垃圾收集算法时(minor gc 或者 full gc),Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互;这些现象多半是由于gc引起。
h. 将数据转移到老年代的情况
1. 分代年龄 到达 15
2. 大对象
3.对象动态年龄判断
这个时候,年龄1+年龄5 的,加起来 占 servivor 总内存区的 55%,这个时候,年龄为5 和 年龄为8 的对象会被直接放到老年代
4.老年代空间分配担保机制
4.1客户端模式
垃圾收集器为客户端模式下的Serial+Serial Old的收集器组合进行内存回收
-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
-XX:SurvivorRatio | Eden区与Survivor区的大小比值 | 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10 |
此时新来的15M 对象,无法放到 Eden 区,进行一次 minor gc, 假设 gc 之后,非垃圾对象为20M,本来应该将这 20M 的非垃圾对象,放到 servior 中的一个,但是servivor0 或者 servivor1 的空间大小只有 10M ,数据放不下,直接将这 20M 的非垃圾对象放到老年代(这里 老年代就是担保人 )
4.2 服务端模式
垃圾收集器为服务端模式(Parallel Scavenge+Serial Old的组合)收集器组合进行内存回收
-XX:+UseParallelGC=8
-XX:+UseParallelGC | Full GC采用parallel MSC(此项待验证) | 选择垃圾收集器为并行收集器.此配置仅对年轻代有效.即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集.(此项待验证) |
新生对象为50M,无法放到Eden 区,会进行minor gc 的操作,在GC前还会进行一次判断,如果要分配的内存>=Eden区大小的一半,那么会直接把要分配的内存放入老年代中。否则才会进入担保机制。
4.3 整体流程
i. 问题
不使用 STW 机制不行么?
如果不将线程挂起,在执行 gc 的过程中,其被标记为 “非垃圾对象” 的数据,有可能变为垃圾对象,又需要重新进行一次垃圾对象的查找,由于对象是动态变化的,这样的话jvm 就需要一直开着 gc 的线程去处理时刻变化的 对象,消耗系统的资源。
jvm 调优,几乎不发生full gc?
让 垃圾对象 在 servivor 区,全部被进行回收,增大年轻代的内存空间。