本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力
四大引用
首先我们需要对引用进行了解,才能更加理解如何分代.
JDK1.2 之后,Java对应用进行了扩充,将引用分为强引用,软引用,弱引用,虚引用,四种引用强度依次减弱
强引用
最传统的引用,指代码中普遍存在的引用赋值 Object o = new Object().
这种情况下,只要强引用关系还存在,永远不会回收!
软引用
用来描述一些还有用,但非必须的对象。
这种关系下,当系统将要内存溢出,会将这些对象列入回收范围中进行第二次回收。如果回收后内存还是溢出,才会跑出内存溢出异常。
JDK1.2 提供SoftReference类实现软引用。
弱引用
也描述非必须对象,但是强度比软引用更弱一些。这种对象只能存活到下次垃圾回收发生为止。当垃圾回收开始工作,无论内存是否足够,都会回收。
WeakReference类
虚引用
最弱的一种引用关系。对象是否用虚引用不会对其生存时间产生影响,也无法通过虚引用生成对象实例。只用于对象回收时受到系统通知。
PhantomReference类
分代模型
基于分代价说
1.弱分代:绝大多数对象朝生夕灭;
2.强分代:熬过越多次垃圾回收过程的对象越难以消灭。
3.跨代引用:跨代引用相对与同代引用来说是极少数。即存在引用关系的两个对象倾向于同时生存或同时消亡。由于老年代的难以消亡,导致被引用的新生代难以被回收,进而容易晋升成老年代而使跨代引用消除。
以此分成两个区域:
新生代
弱分代假说,绝大多数对象都朝生夕灭,活不多垃圾收集过程,每次回收只关注少量存活对象而不是去标记要被回收的对象
存活的对象将逐步晋升到老年代。
如果新生代被老年代引用怎么办
如果去老年代中遍历所有对象来确保可达性分析的正确,会为内存增加极大负担。
根据跨代引用假说:不要为了少量引用去扫描整个老年代。只需要在新生代上建立一个全局的数据结构(记忆集)。这个结构把老年代划分成若干个小块,标识出老年代哪一块内存会存在跨代引用。当发生GC时,才会去这一小块内存中加入GC Roots中进行扫描。
老年代
强分代假说,大多数对象难以消亡,以较低的频率进行垃圾回收
永久代-元空间
类加载器将Class 加载到永久代(1.7)或者元空间(1.8)
1.永久代必须制定大小限制,可能会发生内存溢出
2.元数据可以不设置,无上限(上限为物理内存)
3.字符串常量 在 1.7中存放在永久代 ,1.8中存放在堆中
堆中的逻辑分区
Appel式回收将新生代分成三个区域
新生区百分之90多的对象都会被回收,因此使用Copying算法
- 新生代存放区域
-
Eden
- 这个空间较大,Eden和Survivor 和 的比例 为 8:1
-
Survivor1
-
Survivor2
- 流程
每次分配内存时 使用Eden和一块Survivor区,另一个Survivor用于存放垃圾回收YGC时存活的对象。
当垃圾回收时,将Eden和Survivor0的存活的对象全部复制到Survivor1区域中,然后将Eden和Survivor0的区域全部清空。
- 放不下怎么办
即10%的空间存储 存活对象。 如果不够,那么依赖其他内存区域(老年代)进行分配担保
- 晋升
当一个对象经历过多次垃圾回收还是没有被消灭(年龄足够),就会进入老年区
如果老年代也满了,就会触发一次Full Gc ,全部垃圾回收。
FGC 会对全部堆内存进行垃圾回收,十分消耗资源,并且会STW 停止所有用户活动,尽量减少FGC 。