之前几篇我们介绍了jvm的内存模型以及垃圾回收机制,而本篇我们将介绍几个JVM中对象在分配内存是应该遵循的策略。毕竟,想要去优化程序,不仅要考虑垃圾回收的过程,还要从对象内存分配的角度减少gc的代价。

一、gc日志格式

在这里先介绍一下gc日志的格式,分析gc日志是了解gc过程最直接的方式。对于大量的日志分析,直接查看日志文件当然不方便,我们一般会使用日志分析工具,后边会有介绍,但是对于简短的日志(如十几条),一般直接查看就行了。开启日志输出的JVM参数如下:

-XX:+PrintGCDetails     //打印gc日志
-XX:+PrintGCDateStamps //打印时间
-Xloggc:gc.log //输出路径

对应的日志输出格式如下(采用不同参数或不同虚拟机可能不同,但大同小异):

2014-03-09T18:10:47.639+0800: 36.408: [GC 36.408: [DefNew: 35072K->4352K(39424K), 0.0108988 secs] 75088K->45293K(126848K), 0.0109419 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
2014-03-09T18:10:48.562+0800: 37.331: [Full GC 37.343: [Tenured: 40941K->42890K(87424K), 0.1340821 secs] 62210K->42890K(126848K), [Perm : 36863K->36863K(36864K)], 0.1341736 secs] [Times: user=0.13 sys=0.00, real=0.15 secs]

35072K->4352K(39424K):gc前使用内存大小->gc后使用大小(该区域总大小)
    2014-03-09T18:10:47.639+0800: gc时间
    36.408: gc耗时
    GC 36.408:  表示停顿类型和停顿时间,如果是Full表示gc是Stop-The-World的
    DefNew: Tenured:  Perm : gc发生的区域,DefNew年轻代;Tenured年老代;Perm永久代
    Times: user=0.13 sys=0.00, real=0.15 secs user:用户态耗时;sys:内核态耗时;real:gc从开始到结束消耗的Wall Clock Time耗时(墙钟时间,名字好怪,包括各种非运算的等待耗时和cpu耗时)

二、内存分配策略

1、对象优先在Eden分配

新的对象大多数情况下在Eden去分配,这样在Eden区域没有足够空间时,JVM会首先进行Minor Gc,仅对Eden进行gc,并将符合条件的对象移至年老代。这样如果Eden在gc后满足新对象的空间需求,则能避免进行Full GC。

2、大对象直接进入年老代

这一点十分好理解,多数情况下大对象的生命周期是相对较长的,而大对象如果分配在Eden,不仅占用大量空间,触发gc,还可能会在Eden区/两个Survivor区之间来回复制,十分耗费资源。因此,多数时候对于大对象,应该直接分配至年老代,开始用-XX:PertenureSizeThreshold参数指定直接分配年老代的大小。

3、长期存活的对象进入年老代

一般经过多次Minor GC而不死的对象,继续活下去的可能新更大,因此应该进入年老代。为了判断哪些对象长期存活,虚拟机给每个对象都定义了一个Age计数器,没经历一次Minor GC就+1,可以通过-XX:MaxTenuringThreshold的值来决定age达到多少时进入年老代。

4、动态对象年龄判断

为了能更好地适应不同程序的内存状况,虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 空间中相同年龄所有对象大小的总和大于Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold 中要求的年龄。

5、空间分配担保

在发生 Minor GC 时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于,则改为直接进行一次 Full GC。如果小于,则查看 HandlePromotionFailure 设置是否允许担保失败;如果允许,那只会进行 Minor GC;如果不允许,则也要改为进行一次 Full GC。 前面提到过,新生代使用复制收集算法,但为了内存利用率,只使用其中一个 Survivor 空间来作为轮换备份,因此当出现大量对象在 Minor GC 后仍然存活的情况时(最极端就是内存回收后新生代中所有对象都存活),就需要老年代进行分配担保,让 Survivor 无法容纳的对象直接进入老年代。与生活中的贷款担保类似,老年代要进行这样的担保,前提是老年代本身还有容纳这些对象的剩余空间,一共有多少对象会活下来,在实际完成内存回收之前是无法明确知道的,所以只好取之前每一次回收晋升到老年代对象容量的平均大小值作为经验值,与老年代的剩余空间进行比较,决定是否进行 Full GC来让老年代腾出更多空间。

本篇介绍完毕,下一篇将介绍JVM监控相关的一些工具。

JVM学习总结四——内存分配策略的更多相关文章

  1. 深入理解JVM(5)——垃圾收集和内存分配策略

    1.垃圾收集对象 垃圾收集主要是针对堆和方法区进行. 程序计数器.虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收. 哪 ...

  2. jvm垃圾收集器与内存分配策略

    一.垃圾回收 1.对象是否已经变为垃圾 1.1.引用计数法:给对象添加一个引用计数器,每当有地方引用它时,计数器就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象就是不可能再被使用的. 这 ...

  3. [jvm]垃圾回收与内存分配策略

    一.垃圾回收算法 概述 JVM中,当创建的对象不再被使用的时候,此时我们认为他是无用的“垃圾”:在现代主流的商用jvm中,都是通过可达性分析来判断对象是否存活的.这个算法的基本思想是通过一系列“GCR ...

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

    在前面的Java自动内存管理机制(上)和Java自动内存管理机制(下)中介绍了关于JVM的一些基础知识,包括运行时数据区域划分和一些简单的参数配置,而其中也谈到了GC,但是没有深入了解,所以这里开始简 ...

  5. 深入理解JVM - 垃圾收集器与内存分配策略 - 第三章

    引用计数算法——判断对象是否存活的算法 很多教科书判断对象是否存活的算法是这样的:给对象添加一个引用计数器,每当一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象 ...

  6. jvm垃圾回收器与内存分配策略

    一.判断对象存活的算法 1.引用计数算法 (1)概念:给对象中添加一个引用计数器每当有一个地方引用它时,计数器值加1:当引用失效时,计数器就减1:任何时刻计数器为0的对象就是不可能再被使用的. (2) ...

  7. jvm系列(二)jvm垃圾收集器与内存分配策略

    众所周知,在java语言中,内存分配和回收是由jvm自动管理的.因此内存的分配和回收也是jvm三大功能之一.垃圾收集器(GC)需要完成三件事情: 哪些内存需要回收? 什么时候进行回收? 如何回收? 本 ...

  8. JVM·垃圾收集器与内存分配策略之垃圾回收算法!

    1.垃圾回收算法    1.1.标记-清除算法(Mark-Sweep):             过程分为“标记”和“清除”两个过程.先将所有需要回收的目标统一标记,然后再统一清除.          ...

  9. JVM·垃圾收集器与内存分配策略之对象是否可被回收!

    1.判断对象已经死去/不再被引用.     1.1.引用计数算法:给对象添加引用计数器,有个地方引用就+1,引用失效就-1.任何时刻,引用为0,即判断对象死亡.         1.1.1.优点:实现 ...

随机推荐

  1. Web 项目 中读取专用配置文件

    在 web 开发中,有时我们要为 业务逻辑处理 配置专用的 配置文件,也就是 xml 文件,这样可以极大的方便维护工作,但是读取 专用的配置文件还需要自己写一个方法,在这里,我封装了一个公用 的方法: ...

  2. bzoj1216 [HNOI2003]操作系统

    1216: [HNOI2003]操作系统 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 751  Solved: 419[Submit][Status ...

  3. Centos linux php扩展安装步骤

    使用phpinfo()函数输出PHP信息,然后找到Configuration File (php.ini) apachectl 其设计意图是帮助管理员控制Apache httpd后台守护进程的功能. ...

  4. ionic goto other page or alert

    有时候需要 调试,这是就需要alert 的...可惜的是我不会angular  所以记录一下 .controller('mainctr', function($scope, $window) { $w ...

  5. js的数组申明

    //数组的3种申明方法,如下example //数组是一种object类型 通过typeof 来检查 //example 1 var arr= new Array("h",&quo ...

  6. 【转】DDR3详解(以Micron MT41J128M8 1Gb DDR3 SDRAM为例)

    这两天正在学习FPGA如何控制DDR3的读写,找到一篇个人感觉比较有意义的文章,可以对DDR的内部结构有一个初步的了解.原文出处:http://blog.chinaunix.net/uid-28458 ...

  7. 编程规范 html部分

    不管有多少人共同参与同一项目,一定要确保每一行代码都像是同一个人编写的. HTML 部分 语法 对于属性的定义,确保全部使用双引号,绝不要使用单引号. 为每个 HTML 页面的第一行添加标准模式(st ...

  8. Magento修改css样式

    Magento研究了第四天才开始搞明白怎么运行. 首先对于前端开发来说要修改样式的话需要运行: grunt less:luma 如果提示: 那就说明grunt配置的路径不对,默认是英文的,如果我们用中 ...

  9. JavaScript for...in 语句

    JavaScript for...in 语句 for...in 语句用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作). for ... in 循环中的代码每执行一次,就会对数组的元素或者 ...

  10. Linux程序分析工具:ldd和nm

    ldd和nm是Linux下两个非常实用的程序分析工具.其中,ldd是用来分析程序运行时需要依赖的动态链接库的工具,nm是用来查看指定程序中的符号表信息的工具. 1 ldd 格式:ldd [option ...