jvm优化_day02

垃圾回收介绍
1.什么是垃圾回收?
    程序在执行的过程中,必然会向系统申请内存资源,而已经没有用了的资源,如果不回收掉,最终就会导致内存溢出,因此需要垃圾回收
2.C/C++语言的垃圾回收
	在C/C++语言中,没有自动垃圾回收机制,是通过new关键字申请内存资源,通过delete关键字释放内存资源。如果,程序员在某些位置没有写delete进行释放,那么申请的对象将一直占用内存资源,最终可能会导致内存溢出。
3.Java语言的垃圾回收
	在Java语言中,有了自动的垃圾回收机制,也就是我们熟悉的GC。
垃圾回收算法
常见的垃圾回收算法:
	* 引用计数法
	* 标记清除法
	* 标记压缩法
	* 复制算法
	* 分代算法
	
1.引用计数法的原理:new一个对象A的时候,gc会为该对象分配一个引用计数器;每当有一个其他的对象B持有对象A的引用的时候,对象A的引用计数器的数值+1;每当有一个其他的对象C放弃对对象A的引用的持有权,对象A的引用计数器的数值-1;最终,当执行gc的时候,gc会将所有的引用计数器数值为0(:没有被任何对象引用)的对象回收
1.1 引用计数法的优缺点:
优点:
- 实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。
- 在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报outofmember 错误。
- 区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。
缺点:
- 每次对象被引用时,都需要去更新计数器,有一点时间开销。
- 浪费CPU资源,即使内存够用,仍然在运行时进行计数器的统计。
- 无法解决循环引用问题。(最大的缺点)


2.标记清除法的原理:
	标记清除法分为2个阶段,第一阶段:标记阶段,当执行gc时,引用中除了gc的线程,其他所有的线程全部暂停活动,gc从一个root根节点出发,遍历找出所有被root节点直接或间接引用了的对象,并标记为1,其他没有被root根节点引用到的对象标记为0;第二阶段:清除阶段,清除所有被标记为0的对象,然后重置标记为1的对象为0,唤醒其他的线程
2.1 标记清除法的优缺点:
优点:
- 标记清除算法解决了引用计数算法中的循环引用的问题,没有从root节点引用的对象都会被回收。
缺点:
- 效率较低,标记和清除两个动作都需要遍历所有的对象,并且在GC时,需要停止应用程序,对于交互性要求比较高的应用而言这个体验是非常差的。
- 通过标记清除算法清理出来的内存,碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存是不连贯的。
2.2 GC时,停止其他线程的原因:
	标记清除法执行GC时,需要遍历操作,如果此时出现了对象的新建或者销毁,将会影响GC的判断;比如当我们遍历list集合的时候,如果同时进行增删操作,会发生并发修改异常


3.标记压缩法的原理:
	基本同标记清除法,当进入清除阶段时,首先将存活的对象压缩到内存中的一端,然后将非存活区的所有对象回收
3.1 标记压缩法的优缺点:
优点:
- 解决了标记清除法中造成的内存碎片化的问题
缺点:
- 对对象的压缩移动,影响了性能

4.复制算法的原理:
	将内存一分为2,这两块内存区域完全相同,通常称为from区和to区;GC只对From区的对象进行回收;每次执行GC时,GC会将From区的存活对象copy到To区,然后将From区的数据全部回收;此时这一次的To区就成为了下一次GC的From区,而这一次的From去就成为下一次GC的To区
4.1 复制算法的优缺点:
优点:
- 在垃圾对象多的情况下,效率较高
- 清理后,内存无碎片
缺点:
- 在垃圾对象少的情况下,不适用,如:老年代内存
- 分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低

5.分代算法:
	前面介绍了多种回收算法,每一种算法都有自己的优点也有缺点,谁都不能替代谁,所以根据垃圾回收对象的特点进行选择,才是明智的选择。
	分代算法其实就是这样的,根据回收对象的特点进行选择,在jvm中,年轻代适合使用复制算法,老年代适合使用标记清除或标记压缩算法。
JVM垃圾回收算法
	年轻代使用复制算法,老年代使用标记清除或标记压缩算法
JVM垃圾回收算法原理:
	在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的;当执行GC的时候,GC会将Eden区中存活的对象copy到SurvivorTo区;同时,对SurvivorFrom区的存活对象进行筛选,当对象的年龄到达阀值,就将这个对象放到老年代,没达到阀值的就放入SurvivorTo区;如果SurvivorTo区被填满,就直接都放到老年代中,(可以通过-XX:MaxTenuringThreshold来设置阀值)

在这里插入图片描述

垃圾收集器
	垃圾收集器是对垃圾收集算法的具体实现
		
1.CMS垃圾收集器: 
	CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器(导致内存碎片化严重),该回收器是针对老年代垃圾回收的,通过参数-XX:+UseConcMarkSweepGC进行设置。
* 初始化标记: 从Old区的roots节点出发,找出Old区的所有可达的对象,此过程会stw
* 并发标记: 从初始化标记的可达对象中,找出所有的存活对象;并且,在这段时间,如果有新的对象进入Old区,或者对象不再被roots节点引用,会被标记为dirty,此过程不会stw
* 预清理: 从被标记为dirty的对象中找出所有的roots可达的对象,此过程依然会产生dirty对象
* 重新标记: 从Olds区的roots节点和dirty表出发,再一次扫描出所有的存活的对象,确保所有可达存活对象被标记
* 并发清理: 清除那些没有标记的对象并且回收空间
* 并发重置: 对标记的表和记录dirty的表做清零。并且重置CMS收集器的其他数据结构,等待下一次垃圾回收。
1.1 设置启动参数
-XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -Xms16m -Xmx16m


2.G1垃圾收集器:
	G1垃圾收集器分为三种模式:Young GC, Mixed GC, Full GC;G1垃圾收集器最大的特点便是取消了物理上将内存划分为年轻代,老年代区域,取而代之的是将内存分为了多个Region区域,由这些Region区组成各个逻辑上的年轻代和老年代;为了能在Young GC模式下快速找到roots,每个Region初始化时还会初始化一个Remembered Set,用于跟踪对象的引用,Region区被初始化后默认又会被封为多个512kb的card,因此Remembered Set中便记录某某region中的某某card引用了当前region区中的对象
	通常情况下,new出来的对象都会放到Eden区,但是如果一个对象所占内存超过了分区容量的50%,就会被认为是一个大对象,会直接分配为老年代;但是有可能这个大对象只是一个临时对象,因此G1垃圾收集器又有了一个Humongous区,专门用来存储巨型对象
	
2.1 G1垃圾收集器的三种模式
* Young GC模式:
	当逻辑上的Eden区不够用时触发,G1垃圾收集器会用复制算法将多个Eden区和Survivor From区的存活对象复制到一个新的Survivor To区,如果Survivor To区的内存不够用,就将部分数据直接晋升为逻辑老年区
* Mixed GC模式:
	混合GC,不是Full GC,回收整个Young Region和部分Old Region,默认情况下,当Old Region的已用内存超过总内存的45%,触发Mixed GC;主要分为两阶段,全局并发标记(基本同CMS垃圾收集器,唯一区别是Mixed GC最终拷贝存活对象,而CMS是直接清理),拷贝存活对象
	
2.2 G1收集器相关参数:
- -XX:+UseG1GC
  - 使用 G1 垃圾收集器
- -XX:MaxGCPauseMillis
  - 设置期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到),默认值是 200 毫秒。
- -XX:G1HeapRegionSize=n
  - 设置的 G1 区域的大小。值是 2 的幂,范围是 1 MB 到 32 MB 之间。目标是根据最小的 Java 堆大小划分出约 2048 个区域。
  - 默认是堆内存的1/2000- -XX:ParallelGCThreads=n
  - 设置STW工作线程数的值。将n的值设置为逻辑处理器的数量。n 的值与逻辑处理器(CPU)的数量相同,最多为 8- -XX:ConcGCThreads=n
  - 设置并行标记的线程数。将 n 设置为并行垃圾回收线程数 (ParallelGCThreads)1/4 左右。
- -XX:InitiatingHeapOccupancyPercent=n
  - 设置触发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45%。
eg VM Options: XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails -Xmx256m


2.3 G1收集器优化建议:
- 年轻代大小
  - 避免使用 -Xmn 选项或 -XX:NewRatio 等其他相关选项显式设置年轻代大小。
  - 固定年轻代的大小会覆盖暂停时间目标。
- 暂停时间目标不要太过严苛
  - G1 GC 的吞吐量目标是 90% 的应用程序时间和 10%的垃圾回收时间(9:1)- 评估 G1 GC 的吞吐量时,暂停时间目标不要太严苛。目标太过严苛表示您愿意承受更多的垃圾回收开销,而这会直接影响到吞吐量。
  
2.4 GC可视化工具:
GC Easy 可视化工具
GC Easy是一款在线的可视化工具,易用、功能强大,网站:http://gceasy.io/

-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径
eg:   -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xmx256m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:F://test//gc.log

在这里插入图片描述

循环引用代码
class TestA{
  public TestB b;
}

class TestB{
  public TestA a;
}

public class Main{
    public static void main(String[] args){
        A a = new A();
        B b = new B();
        a.b=b;
        b.a=a;
        a = null;
        b = null;
    }
}

说明:
	A a = new A(),在堆内存中申请了一块内存空间A,并将地址值保存到栈内存变量a中;B b = new B(),在堆内存中申请了一块内存空间B,并将地址值保存到栈内存变量b中;
	a.b=b,将对象b的引用保存到堆内存的A区域中;b.a=a,将对象a的引用保存到堆内存B中;
	a = null,b = null;对栈内存中的变量a,b赋值为null,此时栈内存不再持有堆内存中A,B区域的引用;正常来说,那么它们的引用计数应该为0,但是它们的对内存中依然保持着各自的引用,也就是引用计数器的值始终的变化为:
0-->1-->2-->1
标记清除法原理图

在这里插入图片描述

标记压缩法原理图

在这里插入图片描述

CMS垃圾回收器原理图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值