GC(垃圾回收)
垃圾回收算法:
引用计数法:每个对象都有一个变量用来计数,每引用一次,计数加1 ,释放一次,计数减一。 循环引用不好解决。
可达性分析算法:从一系列GC Roots结点,作为根节点开始向下搜索,搜索所走过的路径称为引用链,如果一个对象没有任何引用链将其与任何一个GC Roots结点相连,就说这个对象式不可达的,准备对它进行垃圾回收
哪些结点可以作为GC Roots结点: (其实就是当前在被使用的对象啊)
虚拟机栈中引用的对象
本地方法栈中引用的对象
方法区中常量引用的对象
方法区中静态变量引用的对象
被标记为不可达的对象,会被立即回收吗?
不会。一个对象被标记为不可达,那么垃圾回收器就准备对它进行垃圾回收,在正式回收之前,会先查看他是否重写了finalize方法,并且在之前没有执行过该方法,如果重写了finalize方法,并且该方法没有被执行过,就会先执行该方法,如果该方法执行完之后,对象与GC Roots通过引用链建立了连接,那么就不会对它进行回收,这个finialize方法只会被执行一次,下一次如果该对象又被标记为不可达的,那么会被直接GC, 不会再去执行finialize方法。
可达性分析算法:
标记清除算法:先判断对象是不是可达的,如果不可达,进行清除,容易产生内存碎片
标记复制算法:将一块内存区域中的可达的对象复制到另一块内存区域,然后清空本内存区域。不会产生内存碎片,但是会造成一定的内存空间的浪费。
标记整理算法:将不可达的对象清除,并且将剩余的对象都在内存中排列好。比较耗时。
分代回收算法: jvm综合了上面三种回收算法的优缺点,采用分代回收算法:
jvm的内存区域分为: 年轻代、老年代、元空间 , 年轻代又分为Eden区和两个Survivor区。
刚创建的对象会被放到年轻代的Eden区,由于新创建的对象往往会经常发生回收,所以年轻代通常采用标记复制算法,及不产生内存碎片,效率又高,缺点就是浪费了一些内存空间。老年代的对象通常来说发生GC的机率要小很多,所以老年代通常采用标记整理算法,既不会产生内存碎片,又不会浪费内存空间,缺点就是相对比较耗时。
年轻代什么时候发生GC: ( young gc, minor gc)
当Eden区的内存区域满了之后就会进行年轻代的gc, 通常也被称作 young gc 或者 minor gc.
老年代发生GC的时候,年轻代会跟着进行GC吗?
当老年代的存储空间容纳不了年轻代的晋升对象时,就会进行Full gc, 当大对象要存储到老年代时,如果老年代容纳不了对象时,就会进行major gc.
对象什么时候进入到老年代:
1) 年轻代采用的是标记复制算法,也就是当Eden区满了的时候,会进行 young gc,这时会将eden区中的可达的对象复制到s1区(当然也可以复制到s2区,因为s1和s2是相对的),然后清空eden区,新创建的对象又会被放到eden区,然后eden区满了之后,又会发生young gc, 将eden区和s1区的可达的对象放到s2区,然后清空eden区和s1区。后面就是不断的进行这个操作。每发生一次young gc,就会将对象的年龄加1,(对象在内存中存储的时候,有标记头,实例数据,对齐填充三部分组成,在标记头中,就有某几位来进行对象年龄的记录,而且那个synchronized用到的重量级锁也在这个标记头被记录着),当对象的年龄到达16时,就会晋升到老年代。(这个年龄是可以进行设置的!!通过 -XX: MaxTenuringThreshold 来设置 )()
2)当survivor区中的某个年龄的对象占用的内存超过了survivor区的一半,那么survivor区中的年龄等于或者大于这个年龄的对象就直接晋升到老年代,不需要再等到达到设置的晋升年龄。
3)大对象会被直接创建在 老年代。 (通过设置 -XX: PretenuringSizeThreshold 参数 来进行设置,当对象占有的内存大于这个设置的内存时就会直接进入到老年代)
【当发生 minor gc前,会先检查老年代的剩余空间大小是否大于年轻代的所有对象所占的内存,如果成立,则进行minor gc,如果不成立,则查看-XX:HandlePromotionFailure参数是否允许担保失败,查看当前老年的剩余空间是否大于之前晋升对象的平均值,如果都成立,进行minor gc,否则进行 full gc】
一些jvm的参数:
jvm的参数都是 -XX: 开头的 有的是用于表示开启或者关闭某个功能 用+ - ,用的表示设置某个参数的具体的值用 =
-XX:+PrintGCDetails 开启打印GC日志功能
-XX: Survivor-Ratio=8 设置eden区和 survivor区的比例为 8:1:1.
设置
-XX: MaxTurnuringThreshold=15 设置晋升到老年代的年龄的大小
-XX: PretenuringSizeThreshold= 3145278 设置对象超过多大内存,就在老年代中创建这个对象 (这里设置的是超过3MB)
-XX: NewRatio=2 设置老年代和年轻代的比例为 2:1
-Xms: (与 -XX:InitialHeapSize相等) 初始堆空间大小
-Xmx: (与 -XX:MaxHeapSize相等)最大堆空间大小 通常设置 -Xms 和 -Xmx为相等的值
-Xss: 设置栈的大小
-Xmn 设置年轻代的大小
-XX:HandlepromotionFailure 是否允许担保失败 (发生在minor gc的时候)
-XX: MetaspaceSize 设置元空间的大小
【注意: -Xms 和 -Xmx是用来设置堆所占用的内存的大小的,也就是创建的对象被存储的区域所占用的内存大小,而-Xss是设置线程所占用的内存的大小的,两者没有关系,都是从操作系统的内存中来占用内存,两者之间不是包含的关系,是平级的关系】
垃圾回收器:
[
Serial / Serial old 用一个线程来进行垃圾回收,回收的时候Stop the world. Serial 标记复制算法 Serial old 标记整理算法
ParNew 相当于多线程版的Serial (采用多线程并行垃圾回收), 也是回收的时候 Stop the world, 采用标记复制算法
Parallel Scavenge: 与ParNew基本一致。 只不过它的目标是 达到一个可控制的吞吐量!(我的jvm就是 用的这个!!!)
Parrallel old: 类似于 多线程版本的 Serial old.
CMS: 并发标记清除。 四个步骤熟记!!!!初始标记、并发标记、重新标记、并发清除 采用的标记清除算法
G1:
]
年轻代:Serial ParNew Parallel Scavenge
Serial: 标记复制算法
都是通过单线程来进行的 标记 和 回收。

ParNew: 标记复制算法
在Serial的基础上,通过多个线程进行垃圾的并行回收。

Parallel Scavenge: 标记复制算法
与ParNew基本一致,只不过是关注的点不一样。Parallel Scavenge关注吞吐量

老年代:
SerialOld: 标记整理
与Serial一样,只不过这个是在老年代,用的标记整理回收算法。单线程进行垃圾的标记和回收。
ParallelOld: 标记整理
与Parallel 一样
CMS: Concurrent Mark Sweep (并发标记清除) 标记清除算法
目标: 最短停顿时间
初始标记 并发标记 重新标记 并发清理
初始标记: 标记与GC Roots直接关联的对象,会stop the world
并发标记: 从初始标记的对象出发,往下进行遍历
重新标记:在并发标记过程中,有写对象的标记状态发生了变化,需要重新标记
并发清除:清除掉不可达的对象。

G1收集器: 标记复制和标记整理算法
G1之前, 要么是整个年轻代 monor gc,要么是整个老年代 Major GC,要么是整个堆 Full GC.
G1 把java堆分成多个大小相等的独立区域(Region),每个Region的角色不是固定的,可以变化。也就是每个Region都可以根据需要作为新生代的Eden区、Survivor区 或者 老年代空间。
、
将内存区域分为一个个的相等的region区,
浙公网安备 33010602011771号