计算机本质
冯诺一曼计算机体系
1、程序的栈
一个线程一个栈.栈空间自动释放,方法结束后,栈帧的指针会下移,所以方法直接从栈里清除
2、程序的堆
堆空间不能进行自动释放,所以需要程序语言自己清除堆垃圾
3、程序中最难调试的bug
-
野指针
-
同一个对象,两个指针,一个释放了,另一个不知道还哪来用
-
同一个指针,不同位置
-
不在指向任何对象的指针
-
NullPointerExcetion
-
-
并发问题
- 多线程访问同一块内存空间
4、计算机语言GC的发展史
-
C/C++
-
手工管理问题
内存忘记释放 会产生内存泄漏,泄漏过多会产生内存溢出OOM
释放多次,会产生及其难以调试的bug,一个线程空间莫名其妙被另一个线程释放掉了
-
开发效率低
-
-
java python go
-
方便管理内存的语言
-
引入GC Garbage Collector 应用线程只管分配,垃圾回收负责回收,降低了开发成本
-
-
rust
-
运行效率超高(asm c c++)
-
不用手工管理内存(没有GC)
-
学习曲线超高
-
变量一夫一妻
-
你只要程序语法通过就不会有bug
-
-
java
- java在new出对象后不需要负责回收,负责回收的是垃圾收集器
垃圾回收器(GC)
1、什么是垃圾
在内存领域说new出对象来会占用内存,当不需要这个对象后需要删掉
2、垃圾收集器如何做的
-
定位垃圾的算法
- 引用计数
- 根可达算法
-
垃圾回收的3种算法
-
标记清除(mark-sweep)
记出可回收的内存 然后直接清掉 这样会使内存碎片化 如果对象过大 会存不上
-
拷贝算法(copying)
把内存划分为两个区域当回收内存时 把存活的对象挪到那部分未使用的内存上 然后全部删掉已使用的内存 优点是回收快,缺点是有一半的内存一直是空的
-
标记整理(mark-compact )
标记出可回收的对象删掉,然后整理存活的对象让他们紧凑排列 优点是不会产生碎片化 缺点是效率低
-
-
垃圾回收的2两种管理模型
-
分带模型
一分为二新生代和老年带:
新生代大量死去少量存活,所以采用拷贝算法
老年代存活率高,回收较少,采用MC和MS 就是标记清除和标记整理算法
幸存区
eden–survivor1–survivor2|tenured
新生带 老年带
当new出对象时会先放入新生代中的eden 需要回收时会清掉eden中的所有对象 把存活对象放入新生代的survivor1中,再一次new对象后需要回收时一样会清掉eden把存活的对象放到survivor2 然后把survivor1中的对象都复制到survivor2上 删掉survivor1循环往复当一个当对象在survivor一直回收不掉时会放入老年带
-
分区模型
堆被划分为一组大小相等的堆区域,每个堆区域都有一个连续的虚拟内存范围
-
3、现阶段jvm10种垃圾回收器
-GC的演化
是随着内存大小的不断增大而演进
初代回收器stw通过垃圾回收器单线程回收到多线程回收,当内存达到几十G上百G后,多线程也会力不从心,因为一般来说cpu的线程数是有限的,当你回收器的线程过多,cpu一下子运行不了那么多线程,cpu就会让线程排队等待,又为了让每个线程都执行到,会进行线程的切换,所以效率提不上去了.
这时就诞生了concurrent GC 指的是GC线程和业务线程并发
-
分带回收器
-
ParNew
-
CMS
CMS 垃圾回收线程和业务线程并发 垃圾回收线程并不是一直运行的,是通过操作系统来调度的linux采用的是cfs调度算法完全公平的算法,回收线程会运行一会停止一会,所以并不会一下子都把垃圾标记出来.这时就需要记录下来标记到了哪里.等回收线程回来时继续标记.标记的方式是叫三色标记算法
-
三色标记算法:(黑色标记:对象已标记,并且已找到这个对象所有的引用,GC线程回来后不扫描;灰色标记:对象被标记,还没来得及找引用,GC线程回来后从这里开始扫描他的引用;白色标记:还没有遍历的节点)内存中有abc3个对象,垃圾CMS启动会把a的引用都找出来把a标记mark word成黑色,当运行到b的时候还没来得及找到b的引用回收线程就停了 这个时候会把b标记成灰色剩下的都标记成白色,当垃圾回收器线程再一次启动时会从灰色开始找到全部引用后标记成黑色,听着是挺合理效率又高,但因为垃圾回收器线程是和业务线程并行的,业务线程有可能做这样一件事,在垃圾回收器把a标记成黑色b标成灰色后的垃圾回收器线程停掉了,业务线程有可能会把b和c之间的引用去掉,然后由a对象重新引用c,因为上一次垃圾回收器把a标记成了黑色b标记成了灰色,当从b扫描时并没有引用c 就把c定义成垃圾 这样a就会出现空指针,这样肯定是不行的,解决方法呢jvm会监控所有标记黑色的对象如果引用了白色对象就会重新标记成灰色 这个方案名叫incremental update CMS有一个巨大的bug当a对象中有两个属性时a.1 引用的是b a.2引用的是b 当垃圾回收器扫描a的时候只扫描了a.1 线程停止因为a.2属性还没有扫描 a这个对象时灰色这个时候a.1的指向变成了d jvm把a对象变灰 回收线程启动会从a.2开始扫描 这个时候d就漏标了 ,为了解决这个问题CMS的remark阶段会进行STW然后重新扫描一下对象树 线程角度是 内存中有abc3个对象,垃圾CMS启动会把a的引用都找出来把a标记mark word成黑色,当运行到b的时候还没来得及找到b的引用回收线程就停了 这个时候会把b标记成灰色剩下的都标记成白色,当垃圾回收器线程再一次启动时会从灰色开始找到全部引用后标记成黑色,听着是挺合理效率又高,但因为垃圾回收器线程是和业务线程并行的,业务线程有可能做这样一件事,在垃圾回收器把a标记成黑色b标成灰色后的垃圾回收器线程停掉了,业务线程有可能会把b和c之间的引用去掉,然后由a对象重新引用c,因为上一次垃圾回收器把a标记成了黑色b标记成了灰色,当从b扫描时并没有引用c 就把c定义成垃圾 这样a就会出现空指针,这样肯定是不行的,解决方法呢jvm会监控所有标记黑色的对象如果引用了白色对象就会重新标记成灰色 这个方案名叫incremental update CMS有一个巨大的bug当a对象中有两个属性时a.1 引用的是b a.2引用的是b 当垃圾回收器扫描a的时候只扫描了a.1 线程停止因为a.2属性还没有扫描 a这个对象时灰色这个时候a.1的指向变成了d jvm把a对象变灰 回收线程启动会从a.2开始扫描 这个时候d就漏标了 ,为了解决这个问题CMS的remark阶段会进行STW然后重新扫描一下对象树 线程角度是
初始标记->并发标记->重新标记->并发清理
-
-
Serial (十 M-几十M 内存大小)
功能 stop-the-world 事件停止 拷贝算法单线程
stw是卡顿的原因 jvm调优主要关注的就是stw的时间不关注吞吐量
-
SerialOld
-
ParallelScavenge(几十兆到上百兆1G)
java1.8默认垃圾回收器
功能 stop-the-world 事件停止 拷贝算法多线程
-
ParallelOld
java1.8默认垃圾回收器
-
Epsilon 测试用的
测试用的
-
G1
java1.7就有不完善,当ps和po不管用时可以尝试G1 java1.9后默认是G1
a.G1算法依然是三色标记,但是方案叫SATB
-
SATB
当B指向D的引用消失后会把这个引用推到GC的堆栈保证白对象D还能被GC找到然后检测有没有黑色对象引用指向D如果有黑对象引用指向D就不把D当垃圾,这里还有一个问题通过A找引用B jvm通过a.变量1=b就能找到,但是b找a是怎么找的呢, 这里呢垃圾回收模型有两种分带模型和分区模型
他把分带模型中的eden,survivor,old分别放在一个个的小分区中分而治之 当然也有一个专门放大对象的分区,每一个小分区head上会有10%-15%的空间记录着有哪些分区里面有引用了指向了这个分区,这我们一般叫Rset(记忆集),从b找a也是通过这个记忆集找
G1是物理上不分带,在逻辑上分代
G1的内存区域不是固定的E或O
G1新老年代比例5%-60%,一般不用手工指定,也不要手工指定,因为这G1观测停顿时间的基准
-
-
ZGC
是个坑,11以上 不是很完善 只支持64位
垃圾清理方案是颜色指针,他跟三色标记的概念不同 最大的是在指针上做标记
-
Shenandoah
-