Jvm研究记录

2017/03/23

自己对jvm研究的一些记录

自己的想法记录

1.jvm有一个分配担保机制,大对象可以直接进入老年代

2.对于游戏来说,大于250B的对象可以直接视为大对象,可以直接进入老年代

3.其实超过50B的对象已经算是一个很大的对象了,假设有2W个用户同时访问,可能短短几秒钟,每个用户创建100个对象,就已经有将近96M的内存开销,200B的对象就会造成384M的开销,根据我对本项目(一个叫风云无双的游戏项目)的研究,虽然低于250B的对象也还是有很多是基本创建出来就不会消亡了,但是低于250B的对象都有可能出现瞬间的大规模创建,而且是瞬间消亡的,所以低于250B的对象还是先放到jvm的Eden区(年轻代),让young gc去筛选对象到老年代,这样会比较好,比较持久的对象早晚还是会进入老年代,而且像这种大对象瞬间创建的情况毕竟还是比较罕见.(这个是根据我负责的游戏项目的同时在线用户的数据分析得出的,但是这算是一个非常极端的情况把,因为就算同时有2W人在线,也不可能有2W人同时做出这种操作的)

4.java7 的服务器就是 ParNew+CMS 这个组合的垃圾回收器是最高效的了

三个算法

复制

年轻代用的

标记-清除

老年代用的,不整理内存碎片

标记-压缩

老年代用的,整理内存碎片

年轻代

  • 年轻代使用的是复制算法

  • 年轻代分为Eden和2个survive区(from space,to space)

  • 分配的对象先进入Eden,然后minor gc之后没有被回收的obj进入from space,Eden直接清空,然后下一次minor gc之后Eden和from space没有被回收的obj都进入to space,from space 和 Eden 直接清空,然后from space 和 to space 角色互换( 意思就是2个survive区的角色互换,原来的from space变成to space了,原来的to space变成from space了,在每次minor gc后to space都是要保持是空的,这种做法就不存在什么内存碎片了,这个很重要 ),当to space满了,就把obj晋升到老年代去

  • 分配担保机制,大对象可以直接进入老年代

CMS

概述

这玩意是用在老年代的,然后它的收集行为并不会full gc,full gc 必须 stop-the-world

CMS GC要决定是否在full GC时做压缩,会依赖几个条件:

第一种条件,UseCMSCompactAtFullCollection 与 CMSFullGCsBeforeCompaction 是搭配使用的;前者目前默认就是true了,也就是关键在后者上。 第二种条件是用户调用了System.gc(),而且DisableExplicitGC没有开启。 第三种条件是young gen报告接下来如果做增量收集会失败;简单来说也就是young gen预计old gen没有足够空间来容纳下次young GC晋升的对象。 上述三种条件的任意一种成立都会让CMS决定这次做full GC时要做压缩。

promotion failed

minor gc 之后,年轻代的obj晋升到老年代的时候,老年代是有足够的内存让这些obj晋升的,但是由于CMS是用的标记-清楚算法实现的,它在进行垃圾回收的过程中是不会进行内存的碎片整理的,这样就导致了虽然老年代是有足够的内存来容纳从年轻代晋升的obj的,但是由于老年代没有一个足够的连续内存空间去容纳从年轻代晋升过来的的某些obj,这样就是引起promotion failed 从而一发一次full gc.

XX:+HandlePromotionFailure 关闭新生代收集担保,java5以前是默认不启用,java6默认启用

concurrent mode failure

CMS是在进行垃圾收集的时候,只有非常短暂的时间会stop-the-world的.就是可以理解为CMS是垃圾收集和用户行为同时进行的,那就意味着,在进行垃圾收集的同时,用户行为还是会产生新的obj(从年轻代晋升过来,CMS是老年代的收集器,这里不谈新生代).为了解决这个问题,CMS会在老年代划出一定比例的内存,在这个比例的内存满了,CMS就会进行老年代的垃圾回收.那就是说,还有一部分空余的内存是可以容纳新的obj的(当然CMS收集过程中,空出来的内存也是可以被新的obj填上的),CMS并不会处理新产生的obj.好,那么问题来了,就CMS的一次垃圾回收的过程总,从年轻代新晋升过来的额obj没地方容纳了,但是CMS还没收集完.就是说垃圾增长的速度比CMS收集的速度要快,然后就会触发一次 concurrent mode failure,进行一次 full gc

promotion failed 和 concurrent mode failure 的总结

promotion failed 和 concurrent mode failure 都会引发full gc 但是 触发的条件不同,这就是它们之间的区别,这可以告诉你jvm什么地方出现了问题,你应该如何去优化.promotion failed是老年代空间足够存放从年轻代晋升过来的对象,但是因为老年代的碎片化太严重,放不下从年轻代晋升过来的大对象所引发的问题.concurrent mode failure是老年代的垃圾回收速度赶不上年轻代晋升所需要的空间所引发的

关于内存的

1) Heap memory: memory within the JVM process that is managed by the JVM to represent Java objects

2) Native memory/Off-heap: is memory allocated within the processes address space that is not within the heap.

3) Direct memory: is similar to native, but also implies that an underlying buffer within the hardware is being shared. For example buffer within the network adapter or graphics display. The goal here is to reduce the number of times the same bytes is being copied about in memory.

Finally, depending upon the OS then extra native allocations (assigning of the memory address space) can be carried out via Unsafe alloc and/or by memory mapping a file. Memory mapping a file is especially interesting as it can easily allocate more memory than the machine currently has as physical ram. Also note, that the total address space limit is restricted by the size of a pointer being used, a 32bit pointer cannot go outside of 4GB. Period.

备注

java 8 之后服务器程序就不用ParNew+CMS了都用G1代替了,G1是java 8 之后 server的默认垃圾收集器而且是吞吐量(性能)最高的垃圾收集器,还有就是 这些参数设置将会被干掉,CMS自己实现了一个foreground collector,现在oracle发现这个玩意效率不行,还导致了代码的混乱,所以要干掉它了,就让