• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

无信不立

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【java虚拟机】之java虚拟机划分

一、java虚拟机划分脑图

 

-XX:NewRatio=1

 表示新生代占1,老年代占1。则新生代占整个堆的1/2。(这个参数一般不会使用)

 

-XX:SurvivorRatio=8 

表示Eden:Survivor=8:1
-Xms20m 表示堆空间初始大小为 20 M
-Xmx20m 表示堆空间最大大小为 20 M
-Xmn10m 表示新生代大小为 10M
-XX:+UseParNewGC 表示年轻代使用serial的多线程版本,它是server模式下首选的新生代收集器,因为能与cms配合(我们生产也是这么配的)
-XX:+UseConcMarkSweepGC

表示老年代用cms收集器(我们生产也是这么配置的)

 

 

-XX:+PrintGC

简单打印gc日志

-XX:+PrintGCDetails 详细打印gc日志
 

 

二、对象的创建

1、对象生成的步骤

       (1)new关键字+类构造器

       (2)根据类名,去常量池查询类是否被加载,如加载则从堆上找一块空闲内存,生成对象。

       (3)不存在,则进行类加载(同步),一个类只会被加载一次,生成类对象,存放至方法区。然后在堆上找一块空闲内存,生成对象。

2、对象分配内存的方式(取决于堆内存是否规整,堆内存是否规整取决于垃圾收集器,其收集器是否存在内存压缩功能)

(1)指针碰撞:空闲内存和使用内存整齐分布,中间有一个指针,需要生成对象,将指针向空闲内存移动一段距离,足够分配对象。

(2)空闲列表:空闲内存和使用内存交错分布,jvm维护内存的列表指针,当需要分配对象时,从列表的指针里找一块足够对象生成的内存进行生成对象。

3、对象分配比较频繁,如果对内存做同步。
 (1)一种方式:cas操作,通过循环重试,进行对象分配。
 (2)一种方式:每个线程预先分配一个块内存,称为本地线程分配缓冲,线程产生的对象,就在该区域内存进行分配,无需进行同步操作。只有当本地线程缓冲使用完,会进行同步,再次分配一块本地线程缓冲,进行对象分配。用-XX:+/-UseTLAB

 

三、垃圾收集器

(1)那些对象可以做GCRoot

==>虚拟机栈(栈帧中的本地变量表)中引用的对象。

==>方法区中静态属性引用的对象

==>方法区中常量引用的对象

==>本地方法栈中JNI(一般说的是native方法)引用的对象。

==>Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。

==>所有被同步锁(synchronized关键字)持有的对象。

==>反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等

 

(2)类卸载(被回收)的前提条件

==》该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例

==》加载该类的classLoader已经被回收

==》该类对应的java.lang.class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

 

 

虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor区,并将对象年龄设为 1。对象在Survivor区中每熬过一次Minor GC,年龄就增加1,当它的年龄增加到一定程度(默认为15)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。

(3)虚拟机垃圾收集器

强弱分代假说

  • 弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的。
  • 强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡。
  • 跨代引用假说(Intergenerational Reference Hypothesis):跨代引用相对于同代引用来说仅占极少数。

垃圾回收类型

  • Minor GC/Young GC:只局限于新生代区域内的收集

  • Major GC/Old GC:目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。另外请注意“Major GC”这个说法现在有点混淆,在不同资料上常有不同所指,读者需按上下文区分到底是指老年代的收集还是整堆收集。

  • Full GC:收集整个Java堆和方法区的垃圾收集

  • Mixed GC:指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。

 

算法实现细节

【STEP1】根节点枚举(停顿):寻找GC-ROOT , Stop The World必须发生。(保证一致性)即使是号称停顿时间可控,或者(几乎)不会发生停顿的CMS、G1、ZGC等收集器,枚举根节点时也是必须要停顿的。

【STEP2】存活对象标记(GC线程和用户线程可并发):可达性分析,三色标记法进行存活对象标记(寻找GC-ROOT标记为黑色,从GC-ROOT开始遍历)

  • 黑色:标识存活的对象。表示对象已经被垃圾收集器访问过,且这个对象的所有引用都已经扫描过。黑色的对象代表已经扫描过,它是安全存活的,如果有其他对象引用指向了黑色对象,无须重新扫描一遍。黑色对象不可能直接(不经过灰色对象)指向某个白色对象。
  • 灰色:标识扫描过程为彻底完成标记的对象。表示对象已经被垃圾收集器访问过,但这个对象上至少存在一个引用还没有被扫描过。
  • 白色:表示对象尚未被垃圾收集器访问过。显然在可达性分析刚刚开始的阶段,所有的对象都是白色的,若在分析结束的阶段,仍然是白色的对象,即代表不可达。

 解决并发标记过程中,对象引用关系发生变化的方案:

  • 增量更新(Incremental Update)【被GC收集器采用】:当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束之后,再将这些记录过的引用关系中的黑色对象为根,重新扫描一次。这可以简化理解为,黑色对象一旦新插入了指向白色对象的引用之后,它就变回灰色对象了。
  • 原始快照(Snapshot At The Beginning,SATB):当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次。这也可以简化理解为,无论引用关系删除与否,都会按照刚刚开始扫描那一刻的对象图快照来进行搜索。

 

垃圾收集器:

 

 

Serial收集器(单线程收集器【标记清除法+标记复制法-新生代】)

 

 

 ParNew收集器(并行收集器【标记清除法+标记复制法-新生代】)

-XX:SurvivorRatio

-XX:PretenureSizeThreshold :线程数

-XX:HandlePromotionFailure

-XX:+UseConcMarkSweepGC 开启CMS收集器

-XX:+/-UseParNewGC  开启和禁用ParNew收集器

 

 

 

 Parallel Scavenge收集器(追求吞吐量的垃圾收集器,并行收集器【标记清除法+标记复制法-新生代】)

 

-XX:MaxGCPauseMillis:每次GC的最长时间

-XX:GCTimeRatio : GC时间占比(1-吞吐量)

 

 

Serial Old收集器(【标记整理法-老年代】)

 

 Parallel Old收集器(【标记整理法-老年代】)

 

CMS收集器(追求停顿时间,并发垃圾收集器【标记清除法-老年代】)

-XX:CMSInitiatingOccupancyFraction:当老年代内存占用达到多少时,触发。百分比。

过程:

1)初始标记(CMS initial mark) => 标记GC-root(Stop the world)
2)并发标记(CMS concurrent mark) => 用户线程和GC线程一起运行,GC线程基于GC-root基于三色法标记垃圾对象
3)重新标记(CMS remark)=>修正并发标记的内容(Stop the world)
4)并发清除(CMS concurrent sweep)

 

Garbage First收集器G1 (年轻代region标记复制算法,老年代并发标记+整理算法(无分代概念,但多个region可以在逻辑上组成年轻代和老年代))

JDK 9及以上版本的HotSpot虚拟机使用参数-XX:+UseConcMarkSweepGC来开启CMS收集器的话,用户会收到一个警告信息,提示CMS未来将会被废弃

G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理,

Region中还有一类特殊的Humongous区域,专门用来存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。每个Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围为1MB~32MB,且应为2的N次幂。

虽然G1仍然保留新生代和老年代的概念,但新生代和老年代不再是固定的了,它们都是一系列区域(不需要连续)的动态集合。G1收集器之所以能建立可预测的停顿时间模型,是因为它将Region作为单次回收的最小单元,即每次收集到的内存空间都是Region大小的整数倍,这样可以有计划地避免在整个Java堆中进行全区域的垃圾收集。更具体的处理思路是让G1收集器去跟踪各个Region里面的垃圾堆积的“价值”大小,价值即回收所获得的空间大小以及回收所需时间的经验值,然后在后台维护一个优先级列表,每次根据用户设定允许的收集停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值收益最大的那些Region,这也就是“Garbage First”名字的由来。这种使用Region划分内存空间,以及具有优先级的区域回收方式,保证了G1收集器在有限的时间内获取尽可能高的收集效率。

-XX:G1HeapRegionSize: 每个region的大小

-XX:MaxGCPauseMillis:每次GC,允许最大的停顿时间长度(单位毫秒)

-XX:G1HeapRegionSize : 设置固定区域存储大对象的阈值。

 

 

过程:

  • 初始标记(Initial Marking)【stop the world】:仅仅只是标记一下GC Roots能直接关联到的对象。
  • 并发标记(Concurrent Marking):从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象
  • 最终标记(Final Marking)【stop the world】:对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。
  • 筛选回收(Live Data Counting and Evacuation)【stop the world】:负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。

 

 

 

(3)内存分配和回收策略

对象分配规则以及对象如何从年轻代晋升至老年代

1、优先在Eden区进行内存分配。(少数情况下,会直接在老年代分配内存,分配规则不是百分百固定的,其细节取决使用哪一种垃圾收集器和jvm中与内存相关的参数)

2、当Eden区没有足够的内存时,会触发一次Minor GC(Yong GC)。

3、当设置了-XX:PretenureSizeThreshold=xx(单位为K)(对象大于xxk时,直接在老年代分配内存)

4、当设置了-XX:MaxTenuringThreshold=m (单位为次数)(对象经历Minor GC的次数大于等于该值,对象从年轻代迁移至老年代)

5、当Survivor空间中相同年龄大小的对象之和>Survivor空间一半大小(from或者to空间),则大于或等于该年龄的对象,从年轻代迁移至老年代。

6、(Minor GC会转换成FullGC)在进行Minor GC前,会进行老年代连续空间大小检查。

【1】jdk1.6之前的规则

    ==>如果老年代连续空间>年轻代对象之和,则正常进行Minor GC

    ==>如果老年代连续空间<年轻代对象之和,并且允许内存担保失败(-XX:HandlePromotionFailure=true)>>>并且老年代连续空间>历届晋升老年代对象之和的平均数,则正常进行Minor GC.

                                                                                                                                                 >>>老年代连续空间<历届晋升老年代对象之和的平均数,则老年代要先进行一次FullGC  (腾出更多空间),再进行Minor GC,让年轻代对象可以平稳晋升到老年代。

   ==>如果老年代连续空间<年轻代对象之和,并且允许内存担保失败(-XX:HandlePromotionFailure=false):老年代要先进行一次FullGC  (腾出更多空间),再进行Minor GC,让年轻代对象可以平稳晋升到老年代。

 【2】jdk1.6之后的规则
     ==>在进行Minor GC前,检查老年代连续空间。如果(连续空间>年轻代对象之和  或者  连续空间>历届晋升老年代对象之和的平均数)其中一个成立,则进行Minor GC

     ==>在进行Minor GC前,检查老年代连续空间。如果(连续空间>年轻代对象之和  或者  连续空间>历届晋升老年代对象之和的平均数)两个都不成立,则老年代要先进行一次FullGC  (腾出更多空间),再进行Minor GC,让年轻代对象可以平稳晋升到老年代。

 

对象分配规则以及对象如何从年轻代晋升至老年代(新总结)

【1】新对象创建的规则

1、eden内存充足:优先在eden区分配内存。

2、eden区内存不足时:触发yongGC,采用复制算法,将eden区存活的对象和from或to中使用中的存活对象汇总,copy至to或from中。清理无用对象腾出空间,存活对象年龄+1;

3、eden区内存不足时:触发完yongGC后,依然无充足内存,eden区的对象也无法复制到from或to中,则直接将eden区中的对象,从年轻代迁移至老年代。

4、大对象规则,如果设置了对象大小大于某个阀值(-XX:PretenureSizeThreshold=xxK)时,大于阀值的对象,直接在old区分配内存。

【2】对象从年轻代晋升至老年代的规则

1、在发生yongGC时,from或to区中的对象年龄值>晋升阀值(-XX:MaxTenuringThreshold=m),该类对象从年轻代迁移至老年代。

2、在发生yongGC时,from或to区中的相同年龄的对象之和的大小>from或to区的一半大小时,大于或等于该年龄的一类对象,从年轻代迁移至老年代。

3、在发生yongGC前,检查老年代的连续空间大小>年轻代eden和(from或to)中对象之和 或者 老年代的连续空间大小>历届晋升对象之和的平均数,正常触发yonggc。伴随对象迁移。

4、在发生yongGC前,检查老年代的连续空间大小<=年轻代eden和(from或to)中对象之和 并且 老年代的连续空间大小<=历届晋升对象之和的平均数,先触发fullGC对老年代回收,再触发yongGC对年轻代回收。

 

 

四、汇总

五、G1垃圾收集器

参考:https://www.cnblogs.com/ASPNET2008/p/6496481.html

 

posted on 2020-01-05 20:18  无信不立  阅读(186)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3