Java内存区域 或 Jvm 内存结构
线程共享 的
- 堆
- 方法区
- 直接内存
- (非运行时数据区的一部分)
线程私有 的 - 程序计数器
- 虚拟机栈
- 本地方法栈
JDK 8 版本之后方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间
GC算法
- 1,标记 -清除算法
- 2,复制算法
- 3,标记-压缩算法
- 4,分代收集算法
一般将堆分为新生代和老年代。 新生代使用: 复制算法 老年代使用: 标记 - 清除 或者 标记 - 整理 算法
G1 GC
- G1 GC是Java HotSpot虚拟机的
低暂停 ,服务器风格 的分代 垃圾收集器,适用于具有大内存的多处理器计算机。G1 GC使用并发 (concurrent)和并行 (parallel)阶段来实现其目标暂停时间并保持良好的吞吐量。 - 全堆操作(例如全局标记)与应用程序线程并行执行。这样可以防止与堆或活动数据大小成比例的中断。
- G1是一个有整理内存过程的垃圾收集器,在回收垃圾的时候会压缩存活对象。不会产生很多内存碎片。
- G1的Stop The World(STW)更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间。
并发(concurrent)和并行(parallel)
- 在单CPU系统中,系统调度在某一时刻只能让一个线程运行,通过不断切换需要运行的线程让其运行的方式就叫并发(concurrent)
- 在多CPU系统中,可以让两个以上的线程同时运行,这种可以同时让两个以上线程同时运行的方式叫做并行(parallel)
- 并发:意味着应用程序同时(并发)在一项以上的任务上取得进展,尽力压榨一个CPU的处理能力,抢占更多的执行时间片
- 并行:应用程序将其任务分解为较小的子任务,这些子任务可以并行处理,例如在多个CPU上同时进行。重点是多个CPU。
G1GC Region
- 传统的GC收集器将连续的内存空间划分为新生代、老年代和永久代(JDK 8去除了永久代/PermGen,引入了元空间Metaspace),这种划分的特点是各代的存储地址(逻辑地址)是连续的。
- G1 GC是一个
区域化 的代垃圾收集器 - G1也是分代管理内存的,他的各代存储地址是不连续的,每一代都使用了n个不连续的大小相同的Region,每个Region占有一块连续的虚拟内存地址。
G1GC 停顿预测模型
- 机器学习听说过吧,这个G1GC里面这个
停顿预测模型 ,就类似这个机器学习 ,自行训练,自己判断每次回收谁,回收多少。 - G1 uses a pause prediction model to meet a user-defined pause time target and selects the number of regions to collect based on the specified pause time target.
- G1 GC是一个响应时间优先的GC收集器,它与CMS最大的不同是,用户可以设定整个GC过程的期望停顿时间,参数-XX:MaxGCPauseMillis指定一个G1收集过程目标停顿时间,默认值200ms,不过它不是硬性条件,只是期望值。那么G1怎么满足用户的期望呢?就需要这个停顿预测模型了。G1根据这个模型统计计算出来的历史数据来预测本次收集需要选择的Region数量,从而尽量满足用户设定的目标停顿时间。
G1GC内部三种形式的GC
G1GC Young GC
Eden区耗尽的时候就会触发新生代收集,新生代垃圾收集会对整个新生代(E + S)进行回收
- 新生代垃圾收集期间,整个应用STW
- 新生代垃圾收集是由多线程并发执行的
- 通过控制年轻代的region个数,即年轻代内存大小,来控制young GC的时间开销。
- 新生代收集结束后依然存活的对象,会被疏散evacuation到n(n>=1)个新的Survivor分区,或者是老年代。
G1GC Mixed GC
- Stop The World
- 选定所有年轻代里的Region,外加根据global concurrent marking统计得出收集收益高的若干老年代Region。在用户指定的开销目标范围内尽可能选择收益高的老年代Region
- Mixed GC不是full GC,它只能回收部分老年代的Region,如果mixed GC实在无法跟上程序分配内存的速度,导致老年代填满无法继续进行Mixed GC,就会使用serial old GC(full GC)来收集整个GC heap
G1GC FULL GC
- Stop The World
- 如果mixed GC实在无法跟上程序分配内存的速度,导致老年代填满无法继续进行Mixed GC,就会使用serial old GC(full GC)来收集整个GC heap。
- G1是不提供full GC的。这个serial old GC full gc是单线程的(在Java 8中)并且非常慢,因此应避免在G1 gc的时候出现这个full gc 。
- 不能觉得用了G1gc收集器之后,Java heap里面的gc不是young gc 就mixed gc,还有这个full gc呢
G1GC 支持的配置参数以及默认值
G1 调优建议
不要显式的设置新生代的大小 (用Xmn或-XX:NewRatio参数)- -XX:ConcGCThreads=n 设置并行标记的线程数。将n设置为并行垃圾回收线程数 (ParallelGCThreads) 的 1/4 左右。
- -XX:ParallelGCThreads=n 设置 STW 工作线程数的值。将 n 的值设置为逻辑处理器的数量。如果逻辑处理器不止八个,则将n的值设置为逻辑处理器数的 5/8 左右。
G1的推荐使用场景
- G1的首要重点是为运行需要大堆且GC延迟有限的应用程序的用户提供解决方案。这意味着堆大小约为6GB或更大,并且稳定且可预测的暂停时间低于0.5秒。
- 如果当前具有CMS或ParallelOld垃圾收集器运行的应用程序具有以下一个或多个特征,则将其切换到G1将非常有益。
- 超过50%的Java堆被实时数据占用。
- 对象分配率或提升率差异很大。
- 不必要的长时间垃圾收集或压缩暂停(长于0.5到1秒)
es7的jvm参数配置
-Xms4g -Xmx4g -XX:+UseG1GC -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:InitiatingHeapOccupancyPercent=45 设置触发全局并发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45%。es7调小这个默认值,希望提前触发全局标记降低full gc的风险 -XX:G1ReservePercent=10 设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险。默认值是 10%。增加或减少百分比时,请确保对总的 Java 堆调整相同的量,es7调大这个值,扩大预留空间。
G1GC实际调优例子
测试程序的jvm参数设置是 -Xmn4G -Xms4G -Xmn64M -XX:+UseG1GC 分析:
- 这使用了G1GC的收集器,但是使用了-Xmn 设置了 NewSize的大小
- 看这个进程的gc情况,每秒都进行10次上下的YGC,很频繁。
- 看OU是在默默的一点点的增长着呢,预计一下:长着长着,就会塞不下,然后触发full gc了
删除掉 -Xmn设置之后的heap情况 分析: - 删掉 -Xmn的设置之后的堆内存分布
- newSize最大可以到 2456MB
- 4096(Heap) = 2576(E) + 4(S) + 1516(O)
- 自动分配之后,Y区占比变大,占比 62%
- 看YGC列,执行次数明显变少几秒钟一次YGCGCT也增长的很慢
- 再观察OU,有增有减。说明有Mixed GC在执行
- SEO各区的大小是浮动的
- 为啥 S0C 和 S0U一直是0?
结论: 用这个例子,实际验证了 G1GC的调优建议中的"不要设置New size的大小",因为这个会限制G1GC内部的停顿预测模型 的工作,他牛就牛在这个模型上。
对比一下CMS GC
S0 和S1是交替在使用,在YGC的时候,有个互相copy的过程
什么时候GC
对象何时进入老年代
- 迭代年龄判断 (都知道)
这个针对的是一个对象长时间存在引用关系,最终在young区来回copy,当年纪大了,就进去old区了。 - 大对象直接进入老年代(不常见)
超过单个region区域一半大小的对象,为避免在young区来回的copy,在new的时候直接给安排到old区。 - YoungGC之后需要移区的对象放不下(基本想不到)
一般推荐使用G1GC,那都是堆比较大的程序,多半都是处理大量数据的程序,既然是大量数据,那么young区满的速度是贼快的,刷刷刷的就满了。 若程序运行支持,前脚创建完对象,后脚里面就使用了,那么YoungGC可以很快的回收young区的内存,Old区也不会出现大量的对象。 这个时候Young区和Old区比例,应该是Young占比比较大才对。 问题就是对象创建完之后,不是很快的结束引用,这个对象就要在Young区来回的copy,当s区不够的时候,他就会被安排到Old区,对象一旦被copy到old区之后, 那就只能被Mixed GC或者Full GC来回收了,但是Mixed GC又不是完全的回收Old区的所有没有被引用的对象,他只是挑选一部分回收价值大的对象去回收。 问题的关键就在这,Mixed GC 1,不是频繁的执行的,2,他还不是回收所有能回收的。当堆内存大量存在可被回收的对象的时候,就会出现浪费的现象。 一直憋着,直到触发Full GC,STW,来一次大清理。
|