2.垃圾收集器与内存分配策略(一)

1. 垃圾收集需要完成的三件事情:

(1)哪些内存需要回收?

(2)什么时候回收?

(3)如何回收?

哪些内存需要回收?

 程序计数器、虚拟机栈、本地方法栈3个区域都是随着线程而生或死,当线程结束时,内存自然跟着回收了。但java堆和方法区则不一样,我们只有在程序运行期间才会知道创建哪些对象,这部分的内存分配与回收是动态的,所以,垃圾收集器主要关注堆和方法区的回收。

什么时候回收?

当然是对象死的时候回收。那如何判断对象是否死亡呢?

(1)引用计数法

   给对象添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器值减1,当值为0时就代表死亡了。但是,主流虚拟机都没有采用这种方式!!原因:它很难解决对象之间互相循环引用的问题。对象A、B之间相互引用的话就不会被回收,这样不行的。

(2)可达性分析算法

主流虚拟机采用这种方式。其基本思想是:通过一系列称为“GC Roots”的对象作为起点,从这些起点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,(就是从GC Roots到这个对象对象不可达),则此对象将会被回收。

那哪些可以作为GC Roots的对象呢?

 1. 虚拟机栈(栈帧中的本地变量表)中引用的对象

 2. 方法区中类静态属性引用的对象

 3. 方法区中常量引用的对象

 4.本地方法栈中引用的对象

2.对象的真正死亡

即使在可达性分析算法中不可达的对象,也并非真正死亡,这时候他们处于“缓刑”状态,还有一次复活的机会。对象经过两次标记之后才算真正死亡。

第一次标记:可达性分析后发现没有与 GC Roots相连接的引用链,将被第一次标记并进行一次筛选,筛选的条件是此对象是否覆盖了finalize()方法或finalize()方法是否已经被虚拟机调用过,如果没有覆盖finalize()方法或者此方法已经被调用过,那就真正宣告死亡了。

第二次标记:判断是否要执行finalize()方法,若需要,此对象会被放入一个F-Queue队列中,并由一个Finalizer线程去执行,如果经过finalize()方法,对象重新链到引用链上,那么他就不会死了。否则,也是真正死亡。finalize()方法是对象逃脱死亡命运的最后一次机会。但是切记:对象的finalize()方法只会被系统调用一次,下一次GC时将不会再执行。

3.关于引用的类型

判断对象是否存活都与“引用”有关,我们希望内存空间足够时,能保留在内存中,如果在GC后空间还紧张,则抛弃这些对象。在jdk1.2后引入了,强引用、软引用、弱引用、虚引用。

强引用:用不会回收被引用的对象

软引用:有用但并非必需的对象,列入二次回收范围

弱引用:描述非必须对象,只能生存到下一次GC发生之前

虚引用:没啥用,也不太清楚

4.永久代的垃圾收集

主要回收两部分:废弃常量和无用的类

如果常量池中没有任何一个引用那就是废弃常量

无用的类:

1. 该类所有的实例都被回收,也就是堆中不存在该类的任何实例

2. 加载该类的ClassLoader已被回收

3. 该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法

5. 如何回收?

三种回收算法:

  (1)标记-清除算法

首先标记处需要回收的对象,在标记后统一回收所有被标记的对象。

不足:一:标记和清除的效率不高;二:标记清理后产生大量不连续的内存碎片,当需要分配大对象时,无法找到足够连续内存

  (2)复制算法

商业虚拟机采用这种算法回收新生代!新生代中对象98%都是朝生夕死的。将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor空间,当回收时,将Eden和用过的Survivor空间中存活的对象复制到另一块Survivor空间上,然后清理掉Eden和用过的Survior空间。若另一块Survivor空间没有足够的空间存放活着的对象,那么老年代进行分配担保,这些对象将直接通过分配担保机制进入老年代。Eden:Survivor的默认比例为8:1,即每次新生代中可用内存为整个新生代内存容量的90%,浪费10%

 (3)标记-整理算法

标记过程与标记-清除算法一样,但后面是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

 (4)分代收集算法

一般把java堆分成新生代和老年代,在新生代中采用复制算法,在老年代由于对象存活率高、没有额外空间进行分配担保,采用标记-清理或标记-整理算法。

6.HotSpot算法实现

(1)枚举根节点

作为GC Roots的节点主要在全局性引用(例如常量或类静态属性)与执行上下文(例如栈帧中的本地变量表)。从GC Roots节点找引用链,如果逐个检查,那必然会消耗很多时间,另外在可达性分析时,整个系统必须停顿,不可在分析过程中对象引用关系还在不断变化。这导致GC进行时必须停顿所有java执行线程(称为Stop The Word),即使是号称不停顿的CMS收集器中,枚举根节点时也必须停顿下来。

停下来后不需要检查所有执行上下文与全局的引用位置。在HotSpot中使用一组称为OopMap的数据结构存储哪些地方存放着对象引用。在类加载完成的时候,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定位置(安全点)记录下栈和寄存器中哪些位置是引用。

(2)安全点

在OopMap的协助下,HotSpot可以快速完成GC Roots枚举,但有个问题,如果为每个指令都生成OopMap,则空间成本就大了。实际上,HotSpot并没有为每个指令生成OopMap,前面提到,只在“特定位置”记录这些信息,这些位置为“安全点”,即程序执行时并非在所有地方都能停下来GC,只有到达安全点才能暂停.安全点的选定基本上以能使程序长时间执行进行选定。

还有一个问题,如何让GC发生时让所有线程都跑到安全点再停顿?

抢先式中断:不采用!!GC发生时,首先中断所有线程,不在安全点上的线程就恢复,让它跑到安全点再中断

主动式中断:GC需要中断线程时,不直接对线程操作,仅仅简单设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时,自己中断挂起,轮询标志的地方与安全点重合,另外再加上创建对象需要分配内存的地方

(3)安全区域

安全点保证了程序执行时,可以遇到安全点,但处于sleep状态的线程呢?无法走到安全地方去中断挂起。那么采用安全区域解决。安全区域指在一段代码中,引用关系不会发生变化,在这个区域中任意地方开始GC都是安全的。

在线程执行到安全区域时,在这段时间发生GC就不用管线程状态。当线程离开安全区域时,首先检查系统是否完成根节点枚举(或者整个GC过程),如果完成了,那线程继续执行,如果没有,那就等待直到可以安全离开安全区域信号为止。

上面介绍的HotSpot的实现过程仅仅是发起了内存回收,具体的执行是由GC收集器决定的,我们来介绍下垃圾收集器。

以下摘自别人博客:

1Serial收集器(新生代)

Serial收集器是最基本的收集器,这是一个单线程收集器,它单线程的意义不仅仅是说明它只用一个线程去完成垃圾收集工作,更重要的是在它进行垃圾收集工作时,必须暂停其他工作线程,直到它收集完成Sun将这件事称之为”Stop the world“

没有一个收集器能完全不停顿,只是停顿的时间长短。

虽然Serial收集器的缺点很明显,但是它仍然是JVMClient模式下的默认新生代收集器。它有着优于其他收集器的地方:简单而高效(与其他收集器的单线程比较),Serial收集器由于没有线程交互的开销,专心只做垃圾收集自然也获得最高的效率。在用户桌面场景下,分配给JVM的内存不会太多,停顿时间完全可以在几十到一百多毫秒之间,只要收集不频繁,这是完全可以接受的。

2ParNew收集器(新生代)

ParNewSerial的多线程版本,在回收算法、对象分配原则上都是一致的。ParNew收集器是许多运行在Server模式下的默认新生代垃圾收集器,其主要在于除了Serial收集器,目前只有ParNew收集器(回收新生代)能够与CMS收集器(回收老年代)配合工作。

3Parallel Scavenge收集器(新生代)

Parallel Scavenge收集器是一个新生代垃圾收集器,其使用的算法是复制算法,也是并行的多线程收集器。

Parallel Scavenge 收集器更关注可控制的吞吐量,吞吐量等于运行用户代码的时间/(运行用户代码的时间+垃圾收集时间)直观上,只要最大的垃圾收集停顿时间越小,吞吐量是越高的,但是GC停顿时间的缩短是以牺牲吞吐量和新生代空间作为代价的。比如原来10秒收集一次,每次停顿100毫秒,现在变成5秒收集一次,每次停顿70毫秒。停顿时间下降的同时,吞吐量也下降了。

停顿时间越短就越适合需要与用户交互的程序;而高吞吐量则可以最高效的利用CPU的时间,尽快的完成计算任务,主要适用于后台运算。

4Serial Old收集器(只有Serial与Serial Old是单线程,老年代)

Serial Old收集器是Serial收集器的老年代版本,也是一个单线程收集器,采用标记-整理算法进行回收。其运行过程与Serial收集器一样。

5Parallel Old收集器(老年代)

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法进行垃圾回收。其通常与Parallel Scavenge收集器配合使用,吞吐量优先收集器是这个组合的特点,在注重吞吐量和CPU资源敏感的场合,都可以使用这个组合。

6CMS 收集器(老年代)

CMSConcurrent Mark Sweep)收集器是一种以获取最短停顿时间为目标的收集器,CMS收集器采用标记--清除算法,运行在老年代。主要包含以下几个步骤:

· 初始标记:工作线程全停(Stop The Word),仅标记GC Roots能直接关联到的对象

· 并发标记:进行GC Roots Tracing的过程

· 重新标记:工作线程全停(Stop The Word),修正并发标记期间,因用户程序继续运行而导致标记变动的那部分对象的标记记录

· 并发清除:清除死亡对象,此阶段会产生浮动垃圾。浮动垃圾就是在并发清除阶段工作线程还在运行,产生的垃圾。

其中初始标记和重新标记仍然需要“Stop the world”。初始标记仅仅标记GC Root能直接关联的对象,并发标记就是进行GC Root Tracing过程,而重新标记则是为了修正并发标记期间,因用户程序继续运行而导致标记变动的那部分对象的标记记录。

由于整个过程中最耗时的并发标记和并发清除,收集线程和用户线程一起工作,所以总体上来说,CMS收集器回收过程是与用户线程并发执行的。虽然CMS优点是并发收集、低停顿,很大程度上已经是一个不错的垃圾收集器,但是还是有三个显著的缺点:

· CMS收集器对CPU资源很敏感。在并发阶段,虽然它不会导致用户线程停顿,但是会因为占用一部分线程(CPU资源)而导致应用程序变慢。

· CMS收集器不能处理浮动垃圾。所谓的浮动垃圾,就是在并发标记阶段,由于用户程序在运行,那么自然就会有新的垃圾产生,这部分垃圾被标记过后,CMS无法在当次集中处理它们,只好在下一次GC的时候处理,这部分未处理的垃圾就称为浮动垃圾。也是由于在垃圾收集阶段程序还需要运行,即还需要预留足够的内存空间供用户使用,因此CMS收集器不能像其他收集器那样等到老年代几乎填满才进行收集,需要预留一部分空间提供并发收集时程序运作使用。要是CMS预留的内存空间不能满足程序的要求,这是JVM就会启动预备方案:临时启动Serial Old收集器来收集老年代,这样停顿的时间就会很长。

.由于CMS使用标记--清除算法,所以在收集之后会产生大量内存碎片。当内存碎片过多时,将会给分配大对象带来困难,这是就会进行Full GC

7G1收集器

G1收集器与CMS相比有很大的改进:

· G1收集器采用标记--整理算法实现。

· 可以非常精确地控制停顿。可让使用者指定一个长度为M毫秒的时间片段,消耗在垃圾收集上的时间不得超过N毫秒

G1收集器可以实现在基本不牺牲吞吐量的情况下完成低停顿的内存回收,这是由于它极力的避免全区域的回收,G1收集器将Java堆(包括新生代和老年代)划分为多个区域(Region),并在后台维护一个优先列表,每次根据允许的时间,优先回收垃圾最多的区域 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
未来社区的建设背景和需求分析指出,随着智能经济、大数据、人工智能、物联网、区块链、云计算等技术的发展,社区服务正朝着数字化、智能化转型。社区服务渠道由分散向统一融合转变,服务内容由通用庞杂向个性化、服务导向转变。未来社区将构建数字化生态,实现数据在线、组织在线、服务在线、产品智能和决策智能,赋能企业创新,同时注重人才培养和科研平台建设。 规划设计方面,未来社区将基于居民需求,打造以服务为中心的社区管理模式。通过统一的服务平台和应用,实现服务内容的整合和优化,提供灵活多样的服务方式,如推送式、订阅式、热点式等。社区将构建数据与应用的良性循环,提高服务效率,同时注重生态优美、绿色低碳、社会和谐,以实现幸福民生和产业发展。 建设运营上,未来社区强调科学规划、以人为本,创新引领、重点突破,统筹推进、整体提升。通过实施院落+社团自治工程,转变政府职能,深化社区自治法制化、信息化,解决社区治理中的重点问题。目标是培养有活力的社会组织,提高社区居民参与度和满意度,实现社区治理服务的制度机制创新。 未来社区的数字化解决方案包括信息发布系统、服务系统和管理系统。信息发布系统涵盖公共服务类和社会化服务类信息,提供政策宣传、家政服务、健康医疗咨询等功能。服务系统功能需求包括办事指南、公共服务、社区工作参与互动等,旨在提高社区服务能力。管理系统功能需求则涉及院落管理、社团管理、社工队伍管理等,以实现社区治理的现代化。 最后,未来社区建设注重整合政府、社会组织、企业等多方资源,以提高社区服务的效率和质量。通过建立社区管理服务综合信息平台,提供社区公共服务、社区社会组织管理服务和社区便民服务,实现管理精简、高效、透明,服务快速、便捷。同时,通过培育和发展社区协会、社团等组织,激发社会化组织活力,为居民提供综合性的咨询和服务,促进社区的和谐发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值