java 之 内存结构-垃圾回收机制算-垃圾收集器

jvm运行时内存划分

1.方法区:类的信息(类的全局变量),常量,静态 (1.8前叫永久区)
2.堆内存:new 出来的对象,数组,
3.java栈 :基本数据类型,局部变量(每个线程独立栈),jvm栈帧(栈帧里面的有个局部变量表)
4.本地方法栈:java调用外部语言(c语言),方法使用native(CAS),安卓开发,应用层 java api 底层 C 语言 JNI
5.程序计数器 :是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
(字符串常量池jdk1.8之前放在发放区 之后放在了堆区)
在这里插入图片描述

下面来说一下堆内存的划分

堆内存划分为:新生代老年代 默认大小为1:2
新生代又划分为:eden区from区to区 默认大小为2:1:1
如下图所示:
如下图片

新生代 :刚出生不久的对象 或者 不经常使用的对象
老年代 :存放比较活跃的对象 经常被引用(使用)的对象

什么是垃圾回收机制?

就是不定时去堆内存中清理不可达对象(没有存活的对象)。不可达的对象并不会马上就会直接回收, 垃圾收集器在一个Java程序中的执行是自动的

如何判断对象是否存活?

引用计数法
引用计数法就是如果一个对象没有被任何引用指向,则可视之为垃圾。这种方法的缺点就是不能检测到环的存在。
首先需要声明,至少主流的Java虚拟机里面都没有选用引用计数算法来管理内存。
什么是引用计数算法:给对象中添加一 个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器值减1.任何时刻计数器值为0的对象就是不可能再被使用的。
那为什么主流的Java虚拟机里面都没有选用这种算法呢?
其中最主要的原因是它很难解决对象之间相互循环引用的问题。

根搜索算法(可达性分析算法)
根搜索算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
那么问题又来了,如何选取GCRoots对象呢?
在Java语言中,可以作为GCRoots的对象包括下面几种:
(1). 虚拟机栈(栈帧中的局部变量表)中强引用的对象。
(2). 元空间中的类静态属性强引用的对象。
(3). 本地方法栈中JNI(Native方法)强引用的对象。
如下图所示
在这里插入图片描述
由上图可以看出 obj8,obj9,obj10 是不可用对象,因为没有任何一个GC Roots与之相连,便是GC Roots不可达的对象,也就是GC需要回收的垃圾对象。

Java 四种引用

强 软 弱 虚
强引用 垃圾回收机制永远不会回收强引用的对象。
软引用:只有当我们堆内存空间不足的时候才会被清理 软引用
弱引用:当我们GC在触发的时候,不管堆内存空间是否足够都会清理弱引用对象。
虚引用:和没有引用是一样,随时都可以回收。
考虑引用避免节约堆内存就可以使用弱引用 比如threadloccal

垃圾回收算法

标记清除算法
该算法有两个阶段。

  1. 标记阶段:找到所有可访问的对象,做个标记
  2. 清除阶段:遍历堆,把未被标记的对象回收
    缺点 :导致内存空间不连续,空间的利用率不高,容易产生空间碎片化
    优点 :效率高
    标记复制算法
    如果jvm使用了copy算法,一开始就会将可用内存分为两块,a区和b区,
    刚开始使用A区,如果A区触发gc了会吧可用的对象放到B区,然后把A的对象给清除,以此类推。
    缺点:不过会浪费一半的内存,降低空间的使用率。
    优点:可以避免出现空间碎片(内存中不连续的空间)

标记整理算法
标记整理算法在标记清除算法之上解决内存碎片化,因为他会将所有的不可达对象排序到一块,
优点:解决内存碎片化
缺点:它整理的时候,对象对应的内存地址会发生变化,中间会stop-the-world ,暂停所有线程
如下图所示
在这里插入图片描述
分代算法
jvm会吧内存分为新生代(eden(2),from(1),to(1))和老年代 ,新对象会放在eden区 ,当Eden区满的时候就触发Minor GC把可用的对象放到from或者to区,然后清空Eden区,并且每次Minor GC(标记复制算法)的时候会来回复制,当对象到达一定的阈值的时候就会放到老年代(大对象也直接放老年代),老年代满了就触发full gc(标记整理算法) 并且也触发 Minor GC

串行 并行 并发
串行gc:单线程gc清理垃圾适合小项目,大项目效率较低()
并行gc:多线程gc清理垃圾适合大项目,小项目反而效率低
并发gc:用户线程和gc线程同时运行 cms/g1

Parnew+cms垃圾收集器和g1垃圾收集器

------------Parnew(新生代)+cms 垃圾处理器(老年代)------------
设置一个阈值堆内存达到一个阈值的时候就清理垃圾

1 初始标记阶段
只标记GC Roots 能直接引用到的对象,速度快stw时间短
2 并发标记阶段
从GC Roots开始找,用户线程和gc线程同时运行,找到它能引用的所有其他对象
3 重新标记阶段
并发标记中,可能会改变GCRoo的引用链,所以需要重新标记一下,会产生stw
4 并发清理
gc线程和用户线程同时清理垃圾,可能会产生浮动垃圾

优点
1.初始化标记与重新标记只需要短暂的stw操作,清理阶段gc线程赫尔用户线程同时运行,不会非常耗时,只会短暂的stw,降低用户线程等待时间
缺点
1.并发清理会导致空间不连续,碎片化问题
2.多线程消耗cpu资源
3.并发清理的时候可能会浮动垃圾
4.大对象无法存放的时候可能引发fullgc
5 CMS收集器只适合于使用老年代

------------g1垃圾收集器------------
整个堆被划分成2048左右个Region
G1中提供了三种模式垃圾回收模式,young gc、mixed gc 和 full gc,在不同的条件下被触发。

young gc(并行多线程,会stw)
发生在年轻代的GC ,就是在eden region区满了的情况下,采用标记复制算法吧对象放到 s区(s0,s1)(会stw),当对象年龄到达一个阈值(默认15)的时候就存放到老年代

mixed gc
当老年代达到一定的阈值的时候,触发mixed gc,回收整个Young Region(新生代区域),部分的老年代区域

full gc

g1收集器回收过程分为三个环节:
1.年轻代GC(Young GC )
如果新生代满了的情况下,先会触发young gc,
2.新生代和并发标记过程( Concurrent Marking)
在新生代进行回收时,进行GCRoot初始化标记与CMS实现原理基本相同,老年代达到堆内存空间阈值时,
3.混合回收(Mixed GC )
如果老年代当老年代达到一定的阈值的时候触发mixed gc,回收整个Young Region(新生代区域),部分的老年代区域

G1收集器Remembered Set*问题(记忆集)

一个region中的对象可能会引入到其他region中的对象,为了避免全局扫描,在每个region中都对应一个Remembered Set(记忆集),使用CarTable记录每个region区相互引用的关系。

G1收集器CSet
筛选出回收收益最高的分区添加到CSet中,回收的时候都是根据CSet进行操作的

G1对比Parnew+cms最大的进步在哪里?
1.G1可以控制stw时间
2.G1基于标记-整理算法, 不会产生空间碎片,分配大对象时不会无法得到连续的空间而提前触发一次FULL GC。

三色标记算法原理

GC如果想查找到存活的对象,根据GCRoot引用链遍历存活对象,GCRoot遍历过程中,分为三种不同颜色。

白色:还没有被访问的对象
灰色:本对象被访问过,属性没有被访问过
黑色:本对象已经被访问多,并且所有属性都被访问过

颜色切换流程
1 初始标记阶段
–只标记GC Roots 引用的对象 变成灰色
2 并发标记阶段
–从GC Roots开始找(灰色),找到它能引用的所有其他对象,都变成黑色
3 重新标记阶段
–解决漏标问题
4 并发清理
–最后把白色的对象都清理

cms 和 g1 区别

区别一: 使用范围不一样
cms只适合老年代还要配合其他收集器一起使用,g1适合老年代和新生代
区别二: STW的时间
CMS收集器stw时间比较小
g1收集器是可预测的停顿时间(建立可预测的停顿时间模型)
区别三: 垃圾碎片
CMS 标记清除(老年代) 容易产生内存碎片
G1 新生代(标记复制),老年代(标记-整理) 不会产生内存碎片,当回增加停顿时间
cms 和 g1 解决漏标问题(最大的区别)
漏标
漏标会导致被引用的对象当成垃圾误删除,这个严重的bug必须解决 一般解决方案有两种:增量更新和原始快照

CMS收集器中处理漏标问题(增量更新):
假如在并发标记阶段如果有 灰色对象与白色对象引用断开,并且有黑色对象白色对象引用,然后在重新标记阶段,它就会把黑色对象变成灰色重新扫描这个黑色对象的引用链。
(这种方案能够确保垃圾都被清理,缺点就是效率非常低)
G1收集器中处理漏标问题(原始快照SATB):
假如有灰色对象与白色对象引用断开,会记录原始快照,如果没有黑色对象去引用这个白色对象也不会被清除,就会产生浮动垃圾,下次在清理
(这种方式效率高,从而人用户线程的停留时间更加短)

jvm 参数性能调优
1 项目堆内存的初始值和最大值保持一致
2 尽量少调用system.gc
3 尽量少放大对象和全局变量,避免触发fullgc

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值