zoukankan      html  css  js  c++  java
  • JVM学习(四)-垃圾回收器和内存分配

    1. 判断对象存活

    回收内存首先需要判断,那些内存需要回收。即需要判断那些对象还存活着,则这些是不需要被回收的。

    (1) 引用计数法

    原理:对象中添加一个引用计数器。被引用则累计。则计数器中数值大于0,则代表仍然被引用,不能被回收。

    缺点:不能解决循环引用的情况。

    (2) 可达性分析法

    原理:从一些称为GCRoots的对象结点开始,向下开始搜索,根据是否能到达来判断对象的存活。其中,从GCRoots对象到目标对象之间的路径称为引用链。

     

    GCRoots的对象包含以下几种:JAVA虚拟栈中的局部变量表中的引用对象、本地方法栈中引用的对象、方法区中静态属性引用的对象、方法区中,常量引用的对象

    2. 垃圾回收算法

    (1) 标记-清除法

    原理:首先先标记需要回收的对象,再标记完成后统一回收所有标记的对象。

    缺点:效率【标记和回收的效率都不高】、空间【只是将需要回收的对象回收,没有将空间系统的整理,所以,会有很多的内存碎片,使用起来不好使用】

    (2) 标记-复制法

    原理:将内存分为两部分S1S2,每次使用其中的一部分,比如S1。等S1空间满了的时候,将其中未被标记出来的【存活的对象】复制到另外一个内存空间S2中,回收整个S1

    缺点:内存使用只有原来的一半。如果存活率较高的情况下,这种算法效率会降低。

    (3) 标记-整理法

    原理:首先标记需要被删除的对象。然后将所有存活的对象向一侧移动。完成后清理掉端边以外的内存。

    (4) 分代收集法

    根据内存存活周期的不同,划分成不同的几块。JAVA堆一般分为新生代和老年代。根据各自年代的特点,选择适合的收集算法。比如:新生代存活率低,因此,新生代选择复制算法。老年代存活率较高,没有额外空间对其进行担保,必行采用“标记-清除”算法或者“标记-清理”算法进行回收

    ① 新生代的回收算法

    1) 新生代内存按照8:1:1的比例分为一个eden区和两个survivor (survivor0,survivor1)区。一个Eden区,两个 Survivor(一般而言)。大部分对象在Eden区中生成。回收时先将eden区存活对象复制到一个survivor0区,然后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另一个survivor1区,然后清空eden和这个survivor0区,此时survivor0区是空的,然后将survivor0区和survivor1区交换,即保持survivor1区为空, 如此往复。

    2) survivor1区不足以存放 edensurvivor0的存活对象时,就将存活对象直接存放到老年代。

    3) 若是老年代也满了就会触发一次Full GC,也就是新生代、老年代都进行回收。

    ② 老年代的回收算大

    1) 在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。

    2) 内存比新生代也大很多(大概比例是1:2),当老年代内存满时触发Major GCFull GCFull GC发生频率比较低,老年代对象存活时间比较长,存活率标记高。

    ③ 永久代的回收算法

    3. 垃圾回收器

    Hotspot 所包含的垃圾回收器。

     

    垃圾回收器从线程运行情况分类三种

    ① 串行回收:serial回收器,单线程回收。全程stw(Stop The World)

    ② 并行回收:名称以parallel开头的回收器【parallel scavenge回收器、parallel old 回收器】,多线程回收。全程(Stop The World)

    ③ 并发回收:CMSG1,多线程分阶段回收。只有某阶段会(Stop The World)

     

    (1) Serial收集器(复制算法)

    新生代单线程收集器,标记和清理都是单线程,优点是简单高效。

    (2) Serial Old收集器(标记-整理算法)

    老年代单线程收集器Serial收集器的老年代版本。

    (3) ParNew收集器(停止-复制算法)

    新生代收集器,可以认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现

    (4) Parallel Scavenge收集器(停止-复制算法)

    并行收集器,追求高吞吐量,高效利用CPU

    (5) Parallel Old收集器(停止-复制算法)

    Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量优先。

    (6) CMS(Concurrent Mark Sweep)收集器(标记-清理算法)

    特点

    A. 只会回收老年代和永久代,不会回收年轻代。

    B. CMS是一种预处理垃圾回收器,它不能等到old内存用尽时回收。需要在内存用尽前完成回收。否则会导致并发回收失败。所以,CMS垃圾回收器在开始工作有一个触发的阈值,默认为老年代或者永久代的92%

    CMS基于“标记-清除”算法实现的。是最常用的垃圾回收器。CMS垃圾回收器的工作原理有7个步骤

    1.初始化标记,会导致stw

    2.并发标记,与用户线程同时运行。

    3.预清理-与用户线程同时运行。

    4.可被终止的预清理,与用户线程同时运行

    5.重新标记,会导致swt

    6.并发清除,与用户线程同时运行。

    7.并发重置状态等待下一次CMS触发,与用户线程同时运行。

    CMS运行流程如下:

    4. GC的种类和触发

    由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GCFull GC

    (1) Scavenge GC

    一般情况,在创建新对象的时候,对Eden申请空间失败的时候,会触发Scavenge GC。清除Eden中非存活对象,将存活对象移动到S1中。然后整理两个S区。这种GC对老年代不影响。对Eden GC的频率比较高,因此,需要效率比较高的算法【复制算法】。

    (2) Full GC

    对整个堆进行整理。包含新生代,老年代。所以比Scavenge GC消耗时间长。所以,需要尽可能少的较少FULL GC的次数。

    触发场景:老年代被写满(Tenured)、持久代被写满(Perm)、System.GC被显示调用。

    5. 内存分配和回收策略

    对象分配的时候,需要选择一片未被使用的空间。同时两个线程需要创建对象。因此,可能产生并发冲突问题。解决的方案有两个。其一:分配空间的时候,保证同一时刻只有一个线程在申请空间。其二:每个线程在堆中获得一块区域。用来创建线程自己的对象。Thread Local Allocation Buffer简称TLAB

    (1) 对象优先在Eden中分配

    (2) 大对象直接进入老年代

    设置参数,申请空间大于设定的阈值,则直接创建在老年代中。

    (3) 长期存活的对象进入老年代

    根据设置晋升老年代的年龄阈值,对象超过设置的阈值,则移动到老年代。

    (4) 动态对象年龄判断

    根据比例选择年龄阈值。相同年龄的对象大小总和大于等于整体S的一半,则当前年龄为阈值。大于或者等于这个年龄的对象可以移动到老年代,而不需要和年龄阈值比较。

    (5) 空间分配担保

    新生代无法分配内存的时候,把新生代对象转移到老年代中,然后把新对象放入腾空的新生代中。

    如果老年代不能做担保【有连续的空间且大于S区中所有对象的总和--S可以全部移动过去】担保失败会引起Full GC。需要尽量避免频发Full GC。所以,需要了解空间分配担保。

    (一)垃圾回收器和内存分配

  • 相关阅读:
    Laya页面嵌套和Scene.destory导致的Bug
    Laya的滚动容器Panel+HBox
    Laya的对象唯一标识
    Android自带的TTS功能
    一步一步学android之控件篇——ListView基本使用
    android surfaceView 的简单使用 画图,拖动效果
    Android 数据分析系列一:sharedPreferences
    Android Service总结
    android中碰撞屏幕边界反弹问题
    Android开发:setAlpha()方法和常用RGB颜色表----颜色, r g b分量数值(int), 16进制表示 一一对应
  • 原文地址:https://www.cnblogs.com/maopneo/p/13947792.html
Copyright © 2011-2022 走看看