jvm之内存管理

jvm体系结构

简要介绍jvm运行时数据区这一块。


jvm有堆、jvm栈、本地方法栈、方法区、pc寄存器。

堆:所有通过new创建的对象及数组都在堆中分配,其大小可通过-Xmx和-Xms参数来控制。堆被划分为新生代和旧生代,新生代有进一步被划分为Eden Space和Survivo Space(通常又称为S0和S1或From和To)

结构图如下:

其中老年代(Old Generation)与新生代(New Generation)的默认比例为为2:1

Eden(伊甸区)与From Space的默认比例为8:1

方法区:存放了要加载类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息。

用户可以通过-XX:PermSize及-XX:MaxPermSize来指定最小值和最大值

本地方法栈:用于支持native方法的执行,存储了每个native方法调用的状态,在SUN JDK的实现中本地方法栈和JVM方法栈是同一个。

JVM栈:每个线程执行每个方法的时候都在栈中申请一个栈帧,每个栈帧包括局部变量和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果。

PC寄存器:用于存储每个线程下一步将执行动作JVM指令的地址,入该方法为native的,则寄存器中不存储任何信息。

内存回收

收集器

引用计数收集器(Reference Counting Collector)

引用计数收集器采用的是分散式管理方式,通过计数器记录对象是否被引用。当计数器对象为零,说明此对象已经不再被使用,于是可进行回收。

引用计数需要在每次对象赋值时进行引用计数器的增减,它有一定的消耗。另外引用计数对于循环引用的场景没有办法实现回收。

因此对于java这种面相对象的会形成复杂引用关系的语言而言,引用计数是非常不适合的,Sun jdk也没有采用这种方式。

跟踪收集器(Tracing Collector)

跟踪收集器采用的是集中式的管理方式,全局记录数据的引用状态。基于一定的条件的触发,执行时需要从跟集合来扫面对象的引用关系,可能会造成应用程序的暂停,主要有复制(Copying)、标记清除(Mark-Sweep)、标记整理(mark-Compact)

复制


算法:从跟集合扫描出存活的对象,并将找到的存活对象复制到一块新的完全未使用的空间中。
1、简单、高效
2、内存利用率低
3、适合作为新生代的垃圾收集算法
4、为什么适合新生代(思考题)?

标记-清除


算法:从根集合开始扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未标记的对象,并进行回收。
1、基础算法、效率不高

2、产生大量内存碎片

标记-整理


算法:标记阶段和标记清除算法一致,再清除时则不同。在回收不存在对象所占用的内存空间后,会将其他所有存活对象都往左端空闲的空间移动,并更新引用其对象的指针。

1、在标记清除基础上,仍需对象移动,成本更高

2、相比标记清除不会产生内存碎片

SUN JDK GC回收策略

新生代可用GC

SUN JDK认为新生代中的对象通常存活时间较短,因此选择基于Copying算法来实现对新生代对象的回收。

在执行复制时,需要一块未使用的空间来存放存活的对象,S0或S1的其中一块用于在Minor GC触发时作为复制的目标空间,当其中一块为复制的目标空间时,另一块中的内容则会被清空。

因此通常又将S0、S1称为From Space和To Space,Sun JDK提供串行GC、并行回收GC和并行GC三种方式来回收新生代对象所占用的内存。

串行GC(Serial GC)

算法:复制(Copy)

在整个扫描和复制过程中采用单线程的方式执行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别,可以通过-XX:UserSerialGC的方式指定

新生代并行回收GC(Parallel Scavenge)

算法:复制

在扫描和复制时均采用多线程的方式来进行,并行回收GC为大的新生代做了很多优化,在多CPU的机器上其回收时间耗费的会比串行方式短,适合于多CPU、对暂停时间要求较短的应用上,并行回收GC是server级别,可用-XX:UserParallelGC来强制指定,并行方式时默认的线程数是根据CPU核数计算。也可以通过-XX:ParallelGCThreads来强制指定

并行GC(ParNew)

算法:复制

并行GC和并行回收GC的区别在于并行GC须配合旧升代使用CMS GC,CMS GC在进行旧生代GC时,有些过程是并发执行GC的。如此时发生Minor GC,需要进行相应的处理,可通过-XX:+UserParNewGC强制指定。

旧生代可用GC

旧生代与新生代不同,对象存活的时间较长,比较稳定,因此采用标记(Mark)算法来进行回收,扫描出存活的对象,然后再回收未被标记的对象,回收后对空出的空间进行合并,要么标记出来便于进行下次分配,减少内存碎片带来的资源消耗。

串行

1、从根集合对象开始扫描

2、遍历整个旧生代空间或持久代空间,找出未被标示的对象,并回收其内存

3、执行滑动压缩,将存活的对象向旧生代空间的开始进行滑动,最终留出一块连续的到结尾处的空间

优缺点:串行执行的过程中为单线程,需要暂停应用并耗时较长。

配置方式:是client模式默认采用的GC方式,也可以通过-XX:UseSerialGC进行指定。

并行Compacting

 1. 将老生代划分为并行线程个数的区域(regions);
 2. 并行进行存活对象扫描和标记;
 3. 单线程对各区域进行扫描,标记需要压缩移动的区域;
 4. 并行进行对象移动和区域不存活对象的回收。
 优缺点:多线程同时操作以及dense prefix优化,会缩短应用暂停时间。但由于老生代较大,在扫描和标识对象上需要花费较长时间。
 配置方式:通过-XX:+UseParallelGC来指定使用Parallel Mark Sweep;通过-XX:UseParallelOldGC来指定使用Parallel Compacting。

并发(CMS:Concurrent Mark-Sweep GC)

 1. 第一次标记(Initial Marking):暂停整个应用,扫描从根集合点到老生代中可直接访问到的对象,并进行标记;
 2. 并发标记(Concurrent Marking):恢复所有应用的线程,同时开始并发对之前标记过的对象进行轮循,以标记这些对象可访问的对象;
 3. 重新标记(Remark):暂停整个应用,扫描在第二步中被改变引用关系或新创建的对象,并进行标记;
 4. 并发收集(Concurrent Sweeping):恢复所有应用的线程,将没有标记的对象进行单线程回收。针对内存碎片,CMS会尽量将相邻的块重新组装成一个块。
优缺点:优点是只有在第一次标记和重新标记阶段需要暂停整个应用,所以能够做到影响应用响应时间很短。缺点是并发标记和并发收集阶段CMS会与应用线程争用CPU资源(用增量CMS模式可以缓解),并且容易产生内存碎片,free-list机制会导致Minor GC效率下降。

 配置方法:通过-XX:UseConcMarkSweepGC来启动老生代CMS GC;通过-XX:+UseCMSCompactAtFullCollection来启动内存碎片整理功能(整理也会暂停应用)。

内存分配与回收策略

1、对象优先在Eden分配
2、大对象直接进入老年代
3、长期存活的对象将进入老年代
4、动态对象年龄判定
5、空间分配担保

参考文献

分布式java应用

jvm学习笔记:http://blog.csdn.net/cutesource/article/details/5904501


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值