目录
GC前言
java与C++之间又一堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进去,墙里面的人想出来。
——–<<深入理解java虚拟机>>
C/C++ 中主要由用户程序代码来回收分配的内存,不存在无用的对象的筛选过程,效率上比垃圾回收机制更胜一筹。
java语言的一些性能上的劣势都是为了换取开发效率。
首先,了解下“stop the world”
stop the world会在执行某一个垃圾回收算法的时候产生,JVM为了执行垃圾回收,会暂停java应用程序的执行,等垃圾回收完成后,再继续运行。
java程序有不规则的停顿现象,其实这就是“stop the world”,停顿的时候JVM是在做垃圾回收。所以尽可能减少stop the world的时间,就是我们优化JVM的主要目标。
回收靠谁来做? —– > GC
什么是GC?
Garbage Collection 垃圾收集(顾名思义,回收垃圾,不需要的对象)
GC要完成的事情:
1,哪些内存需要回收
2,什么时候回收
3,如何回收
我们需要关注的事情:
1,GC作用域
2,GC算法和回收器
3,GC日志
3,优化GC
堆
GC关注的是堆,我们就要先了解堆
注:1.8废弃了永久代 产生元空间(Metaspace)
可达性分析
无用的对象回收,有用的对象存活,那么如何判断对象是否存活?
如上图:
GC ROOTS 到object1 ,object2,object3,object4有通路,即对象可达,则对象存活
GC ROOTS 到object5 ,object6,object7没有通路,即对象不可达,则判定为可回收的对象
在 Java 中 GC Roots 一般包含以下内容:
- 虚拟机栈中引用的对象
- 本地方法栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中的常量引用的对象
垃圾收集算法
1,标记-清除算法
出现的最早
什么叫标记,怎么标记 ? —– > 通过GC ROOTS的可达性分析对需要回收的对象做标记
什么叫清除? —– > 统一清除(回收)所有被标记的对象
不足之处
效率问题 :两次扫描遍历耗时严重,标记和清除两个过程本身的效率也都不高
空间问题:标记清除后会产生大量不连续的内存碎片
2,标记-整理算法
3,复制算法
为了解决效率问题,复制算法横空出世
实现简单 运行高效 内存空间利用率低
[分代收集算法]
新生代:复制算法
老年代:标记-清除算法 标记-整理算法
把堆分为新生代和老年代 ,这样就可以根据各个年代的特点采用最适当的收集算法。
设计时,对象尽量不要进入老年代 ,而是使我们的对象尽量在新生代回收掉(最理想的状态下,所有的对象都不要进入老年代)
新生代 minor GC
老年代 major GC 比新生代慢十倍
full GC是minor GC和major GC的总称
垃圾收集算法和垃圾收集器有什么关系?
垃圾收集算法是理论,垃圾收集器是垃圾收集算法的实现。
垃圾收集器
垃圾收集器之间的连线表示搭配使用
目前流行的是ParNew 和 CMS的搭配使用
Serial Serial Old
串行回收器
应用程序暂停 ,GC线程运行过程中使用单线程进行串行回收
ParNew
应用程序暂停 ,GC线程多线程并发回收
可以指定并发的线程数
Parallel Scavenge Parallel Old
重视吞吐量
应用程序暂停 ,GC线程多线程并发回收
CMS
Concurrent Mark Sweep,并发标记清除。
标记的流程:
1, 初始标记
标记从GC Root可以直接可达的对象;
2,并发标记(和应用程序线程一起)
主要标记过程,标记全部对象;
3,重新标记
由于并发标记时,用户线程依然运行,因此在正式清理前,再做依次重新标记,进行修正。
4,并发清除(和用户线程一起)
基于标记结果,直接清理对象。
并发标记是最主要的标记过程,而这个过程是并发执行的,可以与应用程序线程同时进行,初始标记和重新标记虽然不能和应用程序并发执行,但这两个过程标记速度快,时间短,所以对应用程序不会产生太大的影响。最后并发清除的过程,也是和应用程序同时进行的,避免了应用程序的停顿。
CMS的优点显而易见,就是减少了应用程序的停顿时间,让回收线程和应用程序线程可以并发执行。但它也不是完美的,从他的运行机制可以看出,因为它不像其他回收器一样集中一段时间对垃圾进行回收,并且在回收时应用程序还是运行,因此它的回收并不彻底。这也导致了CMS回收的频率相较其他回收器要高,频繁的回收将影响应用程序的吞吐量。
G1
Garbage First
G1将堆空间划分成了互相独立的区块。
第一时间处理垃圾最多的区块。
优点
1、因为划分了很多区块,回收时减小了内存碎片的产生;
2、G1适用于新生代和老年代,而CMS只适用于老年代。
内存分配策略
GC是回收对象,那对象的创建和内存的分配是怎样一个过程?
虚拟机为新生对象分配内存,对象所需内存的大小在类加载完成后便可以完全确定
分配内存时有两种情况:
1)java堆中的内存是规整的
什么叫规整 —–> 所有用过的内存放在一边,空闲的内存放在另一边,中间放着指针作为分界点的指示器
上述分配方法称 “指针碰撞”
指针可以看作是一个内存空间地址的偏移量
2)java堆中的内存不是规整的
已使用的内存和空闲的内存相互交错,必须维护一个列表,记录哪些内存块可以用
在分配时从列表中找到一块足够大的空间划分给对象,并更新列表上的记录。
上述分配方式称“空闲链表”(Free List)
对象优先在Eden区分配
Eden区空间不够,触发Minor GC(针对新生代的GC)
大对象直接进入老年代
-XX:PretenureSizeThreshold=3145728 3M
上述jvm参数 表示有超过3MB的对象会直接在老年代分配
长期存活的对象直接进入老年代
对象如果从Eden区经过一次Minor GC到Survivor区,age=1
在Survivor区每熬过一次Minor GC,age++
设置晋升老年代年龄阈值
-XX:MaxTenuringThreshold=15
age=15时,进入老年代
动态对象的年龄判定
相同年龄所有对象的大小总和 > Survivor空间的一半
年龄大于或等于该年龄的对象就可以直接进入老年代,无需考虑MaxTenuringThreshold设置的阈值
空间分配担保
Minor GC 之前检查 老年代最大可用连续空间是否>新生代所有对象总空间
参考文章&学习资料
https://www.cnblogs.com/leefreeman/category/1058724.html
https://www.pdai.tech/md/java/jvm/java-jvm-gc.html
http://wearygods.online/jvm-base
说点什么
您将是第一位评论人!